mercredi 13 octobre 2010

Sync C# - Thread Local Storage - utilisation de DataClass

Introduction
L'article "Sync C# - Thread Local Storage - Introduction" expliquait l'utilité et la mise en place d'un TLS anonyme.
L'utilisation de plusieurs slots TLS peut devenir fastidieux a plus forte raison s'il y a beaucoup de données à stocker et à manipuler.
Une alternative élégante est de stocker dans un slot TLS un objet de type "Data" fournissant l'accès aux differentes données via des propriétés (et pourquoi pas, fournir des méthodes de manipulation des données lorsque que c'est approprié).

L'exemple ci-dessous présente cette méthode en stockant un objet de type DataClass dans le slot TLS.

Exemple
Source: Threading_ThreadLocalStorage_DataClass.cs

Cet exemple présente le stockage d'un objet de donnée dans un slot TLS... ainsi que son utilisation dans une des sous-sous-sous fonction de la méthode "Worker" exécutée par le Thread.

La DataClass publie une seule propriété "x" mais cette classe pourrait bien entendu recevoir toutes les autres propriétés utiles à l'exécution de la tâche accomplie par le thread.

Résultats:
Thread:3 DataClass creating
Thread:4 DataClass creating
Thread:3 Set x=9
Thread:3, Loop:0  x:9
Thread:4 Set x=12
Thread:4, Loop:0  x:12
Thread:5 DataClass creating
Thread:5 Set x=15
Thread:5, Loop:0  x:15
Thread:6 DataClass creating
Thread:6 Set x=18
Thread:6, Loop:0  x:18
Thread:7 DataClass creating
Thread:7 Set x=21
Thread:7, Loop:0  x:21
Thread:8 DataClass creating
Thread:8 Set x=24
Thread:8, Loop:0  x:24
Thread:9 DataClass creating
Thread:9 Set x=27
Thread:9, Loop:0  x:27
Thread:10 DataClass creating
Thread:10 Set x=30
Thread:10, Loop:0  x:30
Thread:11 DataClass creating
Thread:11 Set x=33
Thread:11, Loop:0  x:33
Thread:12 DataClass creating
Thread:12 Set x=36
Thread:12, Loop:0  x:36
Press any key to continue...Thread:3, Loop:1  x:10
Thread:4, Loop:1  x:13
Thread:6, Loop:1  x:19
Thread:7, Loop:1  x:22
Thread:5, Loop:1  x:16
Thread:8, Loop:1  x:25
...
Quelques mots d'explication:
  • Le data slot est alloué lors de l'appel du constructeur statique de la DataClass.
    Pour rappel, le slot doit être déclaré une seule fois mais initialisé pour chaque thread.
    Comme il s'agit d'un constructeur statique, il ne sera exécuté qu'une seule fois.
  • Les propriétés de la DataClass sont initialisées lors de l'appel du constructeur non statique.
    Ce constructeur sera normalement appelé pour chacun des threads.
  • La DataClass dispose du getter statique "ThreadedDataClass" qui a pour rôle de retourner une instance de la DataClass.
    Si l'instance de DataClass n'existe pas encore sans le slot TLS, elle est créée à la volée (et donc initialisée avec son constructeur non statique) et stockée dans le slot TLS.
    Sinon, l'instance déjà stockée dans le slot TLS est castée et retournée.
  • Chaque thread utilise uniquement le getter statique DataClass.ThreadedDataClass pour obtenir la référence vers l'objet DataClass allouée pour le thread. c'est magique :-)
  • L'utilisation du TLS est mis en évidence par l'initialisation de la propriété "x" dans le "worker" du thread et sa réutilisation dans la méthode "DoGasp" (après un long chainage d'appel).

Code source:
Source: Threading_ThreadLocalStorage_DataClass.cs
/* This sample demonstrate the usage of *** THREAD LOCAL STORAGE *** to store a DataClass per thread.
   DataClass may be more easy to use but it required to use a static getter on the DataClass.
    
   Each thread receive an isolated data store (which support Messaging, transaction or security token).
   Using LOCAL STORAGE is more appropriate than using method parameter (which can become difficult to maintain).
   Using LOCAL STORAGE is also more appropriate than static field (that share the value for all the threads).

   See PDF "Pratique de .NET 2.0 et de C ♯ 2.0" in french (look for "AllocateDataSlot") that contains a very usefull sample about usage of LocalDataStireSlot.
      http://omega.enstb.org/yannis/pdf/livre2.pdf
   See article "Use LocalDataStoreSlot" on http://www.java2s.com/Tutorial/CSharp/0420__Thread/UseLocalDataStoreSlot.htm
   see article "Use thread-local storage" on http://www.java2s.com/Tutorial/CSharp/0420__Thread/Usethreadlocalstorage.htm
*/
using System;
using System.Collections.Generic;
using System.Threading;

/// <summary>
/// </summary>
public class DataClass 
{
    private static LocalDataStoreSlot tlsSlot = null;
    
    static DataClass() {
        tlsSlot = Thread.AllocateDataSlot(); // Declare the anonymous slot
    }
    
    public DataClass() {
        Console.WriteLine( String.Format( "Thread:{0} DataClass creating", Thread.CurrentThread.GetHashCode()  ) );
        _x = 0;
    }
    
    /// <summary>
    /// Use this method to access the slot from a thread.
    /// If the slot is not yet initialized, the DataClass instance is created and stored in TLS
    /// </summary>
    /// <returns></returns>
    public static DataClass ThreadedDataClass {
        get {
            Object obj = Thread.GetData( tlsSlot );
            if( obj == null ){
                obj = new DataClass();
                Thread.SetData( tlsSlot, obj );
            }
            return (DataClass)obj;
        }
    }
    
    #region DataClass properties 
    private int _x;
    public int x { 
        get {
            return _x;
        } 
        set {
            _x=value;
        } 
    }
    
    #endregion

}

public class MyClass
{
        
    public static void RunSnippet()
    {
        // Create the Threads
        List<Thread> lst = new List<Thread>();
        for( int i = 0; i < 10; i++ ){
              Thread aThread = new Thread( new ThreadStart( Worker ) );
            lst.Add( aThread );
        }        
        
        // Start the threads
        foreach( Thread th in lst )
            th.Start();
    }
    
    public static void Worker(){
        DataClass data = DataClass.ThreadedDataClass;
        DoBlaBla(); // Perform a chaining call to a routine that will modifies the data object
        for( int i = 0; i <= 10; i++ ) {            
            Console.WriteLine( String.Format( "Thread:{0}, Loop:{1}  x:{2}", Thread.CurrentThread.GetHashCode(), i, data.x ) );
            data.x = data.x + 1;
            Thread.Sleep( 50 ); // Help in Context Switching
        }
    }
    
    public static void DoBlaBla()
    {
        DoSnapSnap();
    }
    
    public static void DoSnapSnap()
    {
        DoBlurpBlurp();
    }

    public static void DoBlurpBlurp()
    {
        DoGasp();
    }
    
    public static void DoGasp()
    {        
        DataClass data = DataClass.ThreadedDataClass;
        data.x = Thread.CurrentThread.GetHashCode()*3;
        Console.WriteLine( String.Format( "Thread:{0} Set x={1}", Thread.CurrentThread.GetHashCode(), data.x ) );
    }
    
    #region Helper methods
    
    public static void Main()
    {
        try
        {
            RunSnippet();
        }
        catch (Exception e)
        {
            string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString());
            Console.WriteLine(error);
        }
        finally
        {
            Console.Write("Press any key to continue...");
            Console.ReadKey();
        }
    }

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);    
    }
    
    private static void RL()
    {
        Console.ReadLine();    
    }
    
    private static void Break() 
    {
        System.Diagnostics.Debugger.Break();
    }

    #endregion
}

mardi 12 octobre 2010

Sync C# - Thread Local Storage - Introduction

Description
TLS pour les intimes, Thread Local Storage est une méthode permettant de stocker des informations au niveau du thread (dans un slot) en s'assurant qu'il existe un slot par Thread.
Le slot peut être nommé (ou non). Il doit être déclaré hors du thread (avant qu'il ne démarre) mais doit impérativement être initialisé dans le thread (car chaque dispose de sa propre copie du slot).

Petite note pour signaler que si un TLS nommé est crée, il doit impérativement être libéré par le code.
Le Garbage Collector ne libère que les TLS anonymes.

Mais pourquoi utiliser des Thread Local Storage?
A priori, on pourrait ne pas trouver cela très intéressant.
En effet, la méthode exécutée dans le thread (le worker) peut déclarer des variables locales faciles à manipuler librement... l'avantage n'est pas forcément évident.
Dans la vie réelle, le worker pourrait implémenter un code et un quantité d'objets largement plus massif qu'un exemple. Dans ce cas, la tâche est découpée dans plusieurs méthodes... et c'est la qu'intervient TLS.
Il est bien entendu possible de passer des paramètres et références de méthodes en méthodes mais s'il y a beaucoup d'objets impliquées, cela pourrait vite devenir un véritable cauchemar... rendant la signature des différentes méthodes à peine lisible.
C'est la qu'intervient TLS car il permet de retrouver facilement un référence vers le Slot depuis une quelconque des sous routines.

Le pdf "pratique de .Net 2.0 et de C# 2.0" aborde entre autre ce sujet (rechercher le texte "LocalDataStoreSlot").
Un exemple plus basique mais forcément lisible est disponible sur la page "Use thread-local storage" (la seule erreur de l'auteur est de recréer un objet Random dans le thread, ce qui a pour résultat de reproduire la même série de nombre aléatoire).
L'exemple inclus dans cet article est plus élaboré mais aussi plus proche d'un cas d'utilisation réèl.

L'attribut ThreadStatic... une alternative intéressante
TLS n'est pas la seule méthode possible. Il existe en effet l'attribut [System.ThreadStatic]qui permet de déclaré une variable statique comme ayant une valeur distincte pour chaque thread.
Cette variable doit bien évidemment être initialisée dans le thread et non avant... car sa stockage sera dissociée au démarrage du Thread.
Juste une petite note pour signaler que l'utilisation de ThreadStatic peut être plus rapide que la méthode tls.

Exemple d'utilisation de TLS
L'exemple (repris ci-dessous) utilise un TLS de type anonyme.
La méthode Worker est appelée par le Thread et donc en charge d'initialiser son slot avec une valeur.
Le worker intialise le slot (tlsCounter) qui maintient la donnée d'un compteur de tentative d'essai.
Le worker fait également appel à la méthode WaitForAck() qui est censée vérifier la réception d'un packet d'acknowledgment.
WaitForAck retourne true pour sortir d'une boucle infinie si:
  • Le packet ACK est recu (non codé dans l'exemple)
  • Le nombre de tentative de réception (tlsCounter) à dépassé un certain seuil.
Pour les besoins de l'exemple, la gestion du compteur se fait via TLS.
Cela implique donc que WaitForAck() peut accéder et modifier ce compteur.
Note:
Pour compléter l'exemple, si WaitForAck() recevait le packet d'acknoledgment, il aurait pu en retourner la valeur en utilisant un autre slot TLS (ex: tlsAckPacket).

Source: Threading_ThreadLocalStorage.cs

Résultat:
Thread 4 will check for AckDataPacket. Counter=0
Thread 4 WaitForAck during 7109 ms.
Thread 3 will check for AckDataPacket. Counter=0
Thread 3 WaitForAck during 2554 ms.
Thread 3 will check for AckDataPacket. Counter=1
Thread 3 WaitForAck during 6268 ms.
Thread 4 will check for AckDataPacket. Counter=1
Thread 4 WaitForAck during 337 ms.
Thread 4 will check for AckDataPacket. Counter=2
Thread 3 will check for AckDataPacket. Counter=2
Threads exit
Press any key to continue...

Source:
using System;
using System.Collections.Generic;
using System.Threading;

public class MyClass
{
    static readonly int PERIOD = 3000 ; // 3 secondes entre chaque appel.
    static readonly int WAIT_ACK_MAX_COUNTER = 3 ; // Nbre de fois que l'on va attendre l'ack
    private static LocalDataStoreSlot tlsCounter = Thread.AllocateDataSlot();    
    private static Random rndWaitPeriod = new Random(); // Will be used to generate a random wait périod
    
    public static void RunSnippet()
    {
        Thread thread1  = new Thread( new ThreadStart( Worker ) );    
        thread1.Start();

        Thread thread2  = new Thread( new ThreadStart( Worker ) );    
        thread2.Start();
        
        thread1.Join();
        thread2.Join();
        Console.WriteLine("Threads exit");
    }
        
    private static void Worker() {
        // Intialize Slot and Counter        
        Thread.SetData( tlsCounter, (int)0 );
        // Mimic the reception of a data packet
        do {                
            Thread.Sleep( PERIOD );            
            Console.WriteLine( "Thread {0} will check for AckDataPacket. Counter={1}", Thread.CurrentThread.ManagedThreadId, (int)Thread.GetData(tlsCounter) );            
        } while( !WaitForAck() ); // WaitForAck will query the 
    }
    
    /// <summary>
    /// Wait for an aknowledgment packet.
    /// </summary>
    /// <returns>When WaitForAck should exit because too muck attempt.</returns>
    private static bool WaitForAck(){        
        int waitTime; 
        
        // Extract the COUNTER VALUE from tls
        int currentCounter = (int)Thread.GetData( tlsCounter );
        currentCounter = currentCounter +1;
        Thread.SetData( tlsCounter, currentCounter );
        
        // CHECK EXIT condition based on counter 
        if( currentCounter >= WAIT_ACK_MAX_COUNTER )
            return true; // exit loop
        
        // ... Code checking the packet arrival
        // ... mimic processing with random waiting time
        lock( rndWaitPeriod ) {
            waitTime = rndWaitPeriod.Next(PERIOD*3);
        }
        Console.WriteLine( "Thread {0} WaitForAck during {1} ms.", Thread.CurrentThread.ManagedThreadId, waitTime );        
        Thread.Sleep( TimeSpan.FromMilliseconds(waitTime) );
        
        // Packet not arrived
        return false; 
    }
    
    #region Helper methods
    
    public static void Main()
    {
        try
        {
            RunSnippet();
        }
        catch (Exception e)
        {
            string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString());
            Console.WriteLine(error);
        }
        finally
        {
            Console.Write("Press any key to continue...");
            Console.ReadKey();
        }
    }

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);    
    }
    
    private static void RL()
    {
        Console.ReadLine();    
    }
    
    private static void Break() 
    {
        System.Diagnostics.Debugger.Break();
    }

    #endregion
}

vendredi 8 octobre 2010

Threading en C# - exemple event-based asynchronous pattern (sans réentrance)

Comme précisé dans l'article "Theading en C# - synchronisation et méthodes de threading", voici un exemple d'implémentation event-based asynchronous pattern (utilisant le ThreadPool) sans support de la réentrance.
Note: les exemples sont développés avec Snippet Compiler.

L'implementation du pattern  "Event-based asynchronous pattern" n'est pas des plus simple et doit être scrupuleusement suivit.
Cet exemple est issue d'un article MSDN supportant la réentrance et implémentant le pattern pour un composant (ce qui le cas d'utilisation le plus approprié de ce pattern).
Pour le besoin de l'exemple et du test, j'ai abandonné  l'implémentation de type Composant pour favoriser l'implémentation du pattern dans la classe de test  TestClass.

Code de test
public class MyClass
{
 public static void RunSnippet()
 {
  // Test of the NON recurrent EvebntBased Asynchronous pattern
  //    (cannot be called several times while processing async event)
  TestClass myTest = new TestClass();
  myTest.ComputeTextCompleted += ComputeTextCompletedCallback;
  myTest.ComputeTextAsync( "This Stuff text" );
  
  try {
   myTest.ComputeTextAsync( "This test will fire exception" );   
  }
  catch(Exception e ){
   WL( "Cannot recall non recurrent Async while working!" );
   WL( String.Format("Exception while calling ComputeTextAsync(). {0} with message \"{1}\"", e.GetType().Name, e.Message ));
  }
  
  // Wait while async call is busy
  while( myTest.IsBusy )
   Thread.Sleep( 10 );
  myTest.ComputeTextAsync( "Second test" );

  // Cancel while working
  while( myTest.IsBusy )
   Thread.Sleep( 10 );
  myTest.ComputeTextAsync( "Third test" );
  Thread.Sleep( 500 );
  myTest.CancelAsync();
  
  // Wait to exit
  while( myTest.IsBusy )
   Thread.Sleep( 10 );  
 }
        ...
}

Résultat du code de test
Cannot recall non recurrent Async while working!
Exception while calling ComputeTextAsync(). ArgumentException with message "Task
 ID parameter must be unique
Nom du paramètre : taskId"

ComputeTextCompletedCallback() received computed text :-)
---This Stuff text---


ComputeTextCompletedCallback() received computed text :-)
---Second test---

Press any key to continue...
ComputeTextCompletedCallback() received cancellation :-(

Code Source
Fichier: Threading_EventBasedAsynchronousPattern.cs

Si le code de test démontre le bon fonctionnement de l'ensemble, c'est surtout le code implémentant le pattern qui est le plus intéressant.
Mais ce code n'est pas publié directement dans ce pot car il est assez long. Il est par contre accessible via le lien vers le fichier Threading_EventBasedAsynchronousPattern.cs.

Dans ce code, le traitement est pris en charge par la worker méthode ComputeTextWorker.
Afin de faciliter la compréhension de l'ensemble, le graphique suivant reprend un descriptif des différents appels (avec quelques annotations).

Classe MyClass:
Classe de test (l'appelant) du snippet permettant de tester l'exactitude de l'implémentation du pattern.
Cette classe est utilisée pour faire l'appel à ComputeTextAsync et attend le résultat fournit par l'évènement ComputeTextCompleted.

Classe TestClass:
Implémente le pattern "Event Based Asynchronous Pattern".
C'est le fonctionnement de cette classe qui est testée.

Cliquer pour agrandir
1*: Utilisation du OnCompletedDelegate via une opération asynchrone pour que l'exécution se fasse sur le thread approprié.
2*: La méthode OnComputeTextCompleted sert a déclencher l'évènement ComputeTextCompleted.
3*: Appel la méthode ComputeTextCompleteCallback de MyClass.

jeudi 7 octobre 2010

sp_msForEachDB

Après l'article "sp_msForEachTable, la perle cachée", voici une présentation de sp_msForEachDB.

Cette autre stored procedure made-in Microsoft permet d'exécuter une commande pour chacune des DB présentes sur le serveur.
Bien partique si, par exemple, l'on veut vérifier la taille du transaction log pour chacune des 20 DB.
Bien qu'il soit possible d'extraire cette information des tables systèmes, sp_msForEachDB m'a rendu ce petit service rapidement et facilement.

Le script ci-dessous utiliser sp_msForEachDB pour générer un autre script sql qu'il ne reste plus qu'a exécuter.
L'execution aurait put être faite directement mais une erreur étant si vite arrivée, il faut mieux être prudent et relire le résultat..


sp_msForEachDB 'print "print ''---- Check SpaceUsed ? ------------------------''" ; print "USE [?]" ; print "GO"; print "sp_helpfile" ; print "GO" ; print "print '' ''" ; print "print '' ''" ; '

RBAR - Row By Agonizing Row

Le dernier séminaire du CLUG sur SSIS fut vraiment intéressant. Il faut en autre question de RBAR.

RBAR  identifie un procédé de développement traitent l'information row-by-row, que cela soit au sein de SQL serveur ou dans les applications métier.
Il est en effet courant de voir des architectes logiciel coder le contrôle des contraintes et la logique de mise à jour dans leurs applications plutôt que de coder ces fonctionnalités dans la base de donnée elle même (c'est soit disant plus facile pour eux).
Tout comme il est courant de voir des "select * from table" ou équivalent au sein de l'application.
Si cela est efficace sur des petites bases de données (logiciel en début de vie), cela est ingérable sur les VLDB (very large DB).

Imaginez donc l'ouverture et le passage en revue d'une table de plusieurs millions de records pour vérifier l'existence d'une Primary Key! Sql serveur fait cela bien mieux que nous.
Imaginez la localisation de quelques enregistrements pour les effacer et flagger un champs dans une table parent a peine moins grande... encore une opération que Sql Serveur fera bien plus efficacement que du code logiciel.
Passer un dataset en revue, ou faire un traitement enregistrement par enregistrement (avec, souvent des boucles imbriquées) peut anéantir les performances de votre logiciel et du moteur SQL qui croule sous les opérations. C'est ça le RBAR de la mort qui tue!

Ne jamais oublier
  • Ne jamais oublier qu'une base de données avec des types bien choisis, des définitions de contraintes sera toujours le meilleur choix (plus rapide).
  • Ne jamais oublier que SQL serveur est le mieux placé et le plus rapide pour gérer les contraintes et les modifications en cascades (stored procedure, triggers).
  • Ne jamais oublier qu'il faut être parcimonieux en sélectionnant ses données.
  • Ne jamais oublier qu'il faut traiter les données par ensemble (set-based update) plutôt que ligne-par-ligne (row-by-row), le rapport de performance sur une VLDB peut être supérieur à 1 pour 1000.
Lecture sur le sujet RBAR


L'article RBAR - Row By Agonizing Row (autre lien) de Remy Gregoire fait tès bonne introduction de RBAR et de ses conséquences.
Il discute de l'implémentation de type RBAR dans les application métier et montre, exemple à l'appuis, comment éviter les traitements de type RBAR dans les triggers.

mercredi 6 octobre 2010

Embarcadero RAD Studio XE

Digne successeur de Delphi Studio 2010 (qui est lui fusionné dans Visual Studio), RAD Studio XE revient sur le devant de la scène avec un environnement de développement propre.

En autre, on retrouve dans RAD Studio XE les languages suivants:
  • Environnement de développement propre.
  • C++ Builder, 
  • Delphi, 
  • Prism, 
  • Delphi for PHP
  • Une intégration avec différents systèmes de SubVersion.
  • Une généralisation du module de modeling plus convivial et supportant les diagrammes de séquence (visiblement la grande nouveauté)
Je vous invite à visionner la vidéo d'introduction sur RAD Studio XE disponible sur le blog de Marco Cantu.

dimanche 3 octobre 2010

Urbi - Langage de programmation open-source pour la robotique

Urbi est uneplateforme logiciel open source utilisée pour controler des robots ou des systèmes complexes. Il inclus une librairie de composant C++ appelé UObject qui est fournit avec une API robotique standard, cette API décrivant des moteurs, des censeurs et bien entendu des algorithmes.
L'environnement inclus également Urbitscript un langage de scripting permettant de faire interagir les composants entre-eux mais surtout, il permet de décrire des comportements de haut-niveau. Pour faciliter les tâches de développement, Urbiscript est un langage de type sémantique et événementiel mais le plus important étant encore que le langage est prévu et tire parti du parallélisme d'exécution.

Faire quelques lectures sur Urbi ramène inévitablement sur paraléllisme coopératif basé sur les coroutines (ou l'on retrouve les notions de Yield return et de générateurs).

Site Officiel: UrbiForge

Le langage Urbi associé au Kit de montage robotique BioLoid semble offrir un environnement d'apprentisage très intéressant. En effet, le kit Bioloid offre de nombreuses possibilités de montages, en témoigne les vidéos présentent sur cette page.

Quelques tutoriaux et documentation


Info complémentaires
En provenance de cet article paru sur Linux.Org :
Urbi est un framework de développement pour la robotique visant à standardiser et simplifier l'écriture de modules et comportements pour les robots, en les rendant réutilisables et en facilitant l'interaction entre robots hétérogènes.

Il comprend:

  • Un modèle de composants C++ avec gestion de dataflow : UObject ;
  • Un middleware permettant aux composants d'interagir localement ou en réseau ;
  • Un langage de script parallèle et événementiel, urbiscript, pour orchestrer les interactions entre composants ;
  • Un environnement d'exécution faisant le lien entre les composants et urbiscript


D'un point de vue technique, Urbi est implémenté en C++ avec un modèle de parallélisme coopératif basé sur les coroutines, et fonctionne sous Linux, Mac OS, et de nombreuses autres variétés d'Unix, et Windows. Les composants se présentent sous forme de bibliothèques dynamiques pouvant être soit chargées à chaud dans un environnement Urbi, soit lancées dans des processus autonomes se connectant par le réseau, ce qui permet par exemple de déporter des calculs de façon transparente. L'environnement permet également d'ouvrir des sessions urbiscript pour exécuter des commandes à la volée, pour modifier les comportements ou piloter ponctuellement le robot par exemple.

Urbi est en développement depuis 2006 au sein de Gostai et provient originellement du monde académique. Seule l'API UObject était jusqu'à maintenant distribuée sous licence open-source, permettant à quiconque de développer des composants C++ utilisables depuis Urbi. Depuis mai 2010, le noyau du framework - comprenant l'interpréteur urbiscript, son scheduler et l'environnement d'exécution des UObjects - est à son tour passé open source sous licence Affero GPL v3.

Ce changement de licence permet aux utilisateurs d'utiliser Urbi avec l'assurance de pouvoir l'adapter à leur besoin et leur offre une indépendance vis-à-vis de Gostai. Gostai accepte les soumissions de patchs venant de l'extérieur pour une intégration éventuelle après revue.