# Introduction

Ce didacticiel est destiné aux utilisateurs maîtrisant la programmation en C ou C++, ainsi que l'architecture de la Simple Virtual Machine.

Dans ce didacticiel, vous allez créer et manipuler des séquenceurs de la machine virtuelle.

Le temps de lecture de ce didacticiel est estimé à 25 minutes.

# Mise en place

Pour commencer, créez le canevas de l'extension dans le fichier sequenceurs.svm_plugin en utilisant ce code :

    PLUGIN sequencer

    DEFINE

# Définition d'un séquenceur

## Structure

Un séquenceur doit être capable de gérer plusieurs noyaux, en règle générale : pour cela, une structure de données servant à la gestion des noyaux peut être très utile. Le générateur d'extension offre des facilités pour définir une telle structure. Modifiez le code de l'extension :

    PLUGIN sequencer

    DEFINE

    SEQUENCER sequencer.instance
    %{
    	SVM_Kernel _main;
    	bool _interrupted;
    	SVM_Kernel _success;
    	SVM_Kernel _failure;
    %}

Comme pour les types extensions, le contenu de la structure du séquenceur suit la déclaration de l'objet séquenceur. Ici, la struct C/C++ générée a pour type le nom formé de la chaine fixe "sequencer_" suivie du nom de l'entrée du séquenceur (ce qui donne ici le type sequencer_instance).

## Construction et destruction

Pour construire et détruire cette structure, deux fonctions doivent être définies. Modifiez le code de l'extension :

    PLUGIN sequencer

    DEFINE

    SEQUENCER sequencer.instance
    %{
    	SVM_Kernel _main;
    	bool _interrupted;
    	SVM_Kernel _success;
    	SVM_Kernel _failure;
    %}
    create default:
    %{
    	object->_main = nullptr;
    	object->_interrupted = false;
    	object->_success = nullptr;
    	object->_failure = nullptr;
    %}
    delete default:
    %{
    	if(object->_main) VARIABLE_LOCAL(object->_main);
    	if(object->_success) VARIABLE_LOCAL(object->_success);
    	if(object->_failure) VARIABLE_LOCAL(object->_failure);
    %}

Les options `default` permettent de générer du code de construction et destruction classique.

## Politique de séquencement

La prochaine fonction à définir contient la politique de séquencement, et par conséquent, est la fonction centrale du séquenceur. Modifiez le code de l'extension :

    PLUGIN sequencer

    DEFINE

    SEQUENCER sequencer.instance
    %{
    	SVM_Kernel _main;
    	bool _interrupted;
    	SVM_Kernel _success;
    	SVM_Kernel _failure;
    %}
    create default:
    %{
    	object->_main = nullptr;
    	object->_interrupted = false;
    	object->_success = nullptr;
    	object->_failure = nullptr;
    %}
    delete default:
    %{
    	if(object->_main) VARIABLE_LOCAL(object->_main);
    	if(object->_success) VARIABLE_LOCAL(object->_success);
    	if(object->_failure) VARIABLE_LOCAL(object->_failure);
    %}
    current object:
    %{
    	if(object->_main and ::svm_kernel_is_runnable(svm,object->_main)) return object->_main;
    	if(not object->_interrupted)
    	{
    		if(object->_success and ::svm_kernel_is_runnable(svm,object->_success)) return object->_success;
    	}
    	else
    	{
    		if(object->_failure and ::svm_kernel_is_runnable(svm,object->_failure)) return object->_failure;
    	}
    	return nullptr;
    %}

L'option `object` permet de générer du code de conversion en C/C++ pour avoir un accès direct à l'objet du séquenceur dans la variable magique `object`.

Le fonctionnement de cette fonction est plutôt simple : lorsqu'elle est invoquée, elle doit simplement retourner le prochain noyau à exécuter. Lorsque plus aucun noyau ne doit être exécuté, la fonction doit simplement renvoyer un pointeur C/C++ nul. Cela met alors fin à l'exécution du processus contenant le séquenceur.

Il est fortement conseillé d'écrire une fonction de séquencement qui soit non bloquante, sous peine de bloquer l'exécution du code dans la machine virtuelle.

## Attachement et détachement

Pour qu'un noyau soit pris en charge par un séquenceur, il est obligatoire de l'attacher à ce séquenceur. De même, lorsqu'un noyau arrive en fin de vie ou sur requête explicite, le noyau peut être détaché pour être soustrait au séquenceur. Modifiez le code de l'extension :

    PLUGIN sequencer

    DEFINE

    SEQUENCER sequencer.instance
    %{
    	SVM_Kernel _main;
    	bool _interrupted;
    	SVM_Kernel _success;
    	SVM_Kernel _failure;
    %}
    create default:
    %{
    	object->_main = nullptr;
    	object->_interrupted = false;
    	object->_success = nullptr;
    	object->_failure = nullptr;
    %}
    delete default:
    %{
    	if(object->_main) VARIABLE_LOCAL(object->_main);
    	if(object->_success) VARIABLE_LOCAL(object->_success);
    	if(object->_failure) VARIABLE_LOCAL(object->_failure);
    %}
    current object:
    %{
    	if(object->_main and ::svm_kernel_is_runnable(svm,object->_main)) return object->_main;
    	if(not object->_interrupted)
    	{
    		if(object->_success and ::svm_kernel_is_runnable(svm,object->_success)) return object->_success;
    	}
    	else
    	{
    		if(object->_failure and ::svm_kernel_is_runnable(svm,object->_failure)) return object->_failure;
    	}
    	return nullptr;
    %}
    attach object:
    %{
    	if(argc==0)
    	{
    		if(object->_main) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_main = kernel;
    		return TRUE;
    	}
    	SVM_Boolean b = ARGV_VALUE(0,boolean);
    	if(b)
    	{
    		if(object->_success) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_success = kernel;
    	}
    	else
    	{
    		if(object->_failure) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_failure = kernel;
    	}
    	return TRUE;
    %}
    detach object:
    %{
    	if(kernel==object->_main)
    	{
    		VARIABLE_LOCAL(kernel); object->_main = nullptr;
    		object->_interrupted =::svm_kernel_get_state(svm,kernel)==INTERRUPTED;
    		return TRUE;
    	}
    	if(kernel==object->_success)
    	{
    		VARIABLE_LOCAL(kernel); object->_success = nullptr;
    		return TRUE;
    	}
    	if(kernel==object->_failure)
    	{
    		VARIABLE_LOCAL(kernel); object->_failure = nullptr;
    		return TRUE;
    	}
    	return FALSE;
    %}

La fonction d'attachement a deux possibilités :

- soit elle accepte le noyau : elle stocke la variable Simple Virtual Machine du noyau attaché dans la structure du séquenceur sous forme de variable globale, puis renvoie la valeur `TRUE`,
- soit elle rejette le noyau : elle renvoie la valeur `FALSE`.

La fonction de détachement a également deux possibilités :

- soit le noyau était attaché au séquenceur : elle retire la variable noyau en la repassant en variable Simple Virtual Machine locale, puis renvoie la valeur `TRUE`,
- soit le noyau n'était pas attaché au séquenceur : elle renvoie la valeur `FALSE`.

## Affichage

La dernière fonction à créer sert à indiquer l'état du séquenceur. Modifiez le code de l'extension :

    PLUGIN sequencer

    DEFINE

    SEQUENCER sequencer.instance
    %{
    	SVM_Kernel _main;
    	bool _interrupted;
    	SVM_Kernel _success;
    	SVM_Kernel _failure;
    %}
    create default:
    %{
    	object->_main = nullptr;
    	object->_interrupted = false;
    	object->_success = nullptr;
    	object->_failure = nullptr;
    %}
    delete default:
    %{
    	if(object->_main) VARIABLE_LOCAL(object->_main);
    	if(object->_success) VARIABLE_LOCAL(object->_success);
    	if(object->_failure) VARIABLE_LOCAL(object->_failure);
    %}
    current object:
    %{
    	if(object->_main and ::svm_kernel_is_runnable(svm,object->_main)) return object->_main;
    	if(not object->_interrupted)
    	{
    		if(object->_success and ::svm_kernel_is_runnable(svm,object->_success)) return object->_success;
    	}
    	else
    	{
    		if(object->_failure and ::svm_kernel_is_runnable(svm,object->_failure)) return object->_failure;
    	}
    	return nullptr;
    %}
    attach object:
    %{
    	if(argc==0)
    	{
    		if(object->_main) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_main = kernel;
    		return TRUE;
    	}
    	SVM_Boolean b = ARGV_VALUE(0,boolean);
    	if(b)
    	{
    		if(object->_success) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_success = kernel;
    	}
    	else
    	{
    		if(object->_failure) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_failure = kernel;
    	}
    	return TRUE;
    %}
    detach object:
    %{
    	if(kernel==object->_main)
    	{
    		VARIABLE_LOCAL(kernel); object->_main = nullptr;
    		object->_interrupted =::svm_kernel_get_state(svm,kernel)==INTERRUPTED;
    		return TRUE;
    	}
    	if(kernel==object->_success)
    	{
    		VARIABLE_LOCAL(kernel); object->_success = nullptr;
    		return TRUE;
    	}
    	if(kernel==object->_failure)
    	{
    		VARIABLE_LOCAL(kernel); object->_failure = nullptr;
    		return TRUE;
    	}
    	return FALSE;
    %}
    print object:
    %{
    	std::ostringstream oss;
    	oss << "Main kernel: ";
    	if(object->_main)
    	{
    		SVM_String m = ::svm_kernel_print(svm,object->_main);
    		oss << RAW_STRING(m);
    	}
    	oss << std::endl << "Main kernel " << (object->_interrupted?"":"not ") << "interrupted"
    	    << std::endl << "Success kernel: ";
    	if(object->_success)
    	{
    		SVM_String s = ::svm_kernel_print(svm,object->_success);
    		oss << RAW_STRING(s);
    	}
    	oss << std::endl << "Failure kernel: ";
    	if(object->_failure)
    	{
    		SVM_String f = ::svm_kernel_print(svm,object->_failure);
    		oss << RAW_STRING(f);
    	}
    	oss << std::endl;
    	return NEW_STRING(oss.str());
    %}

Vous pouvez générer et compiler l'extension. Cependant, plutôt que d'utiliser directement l'extension, il est conseillé de regarder le code produit par le générateur d'extension.

Cette fonction doit simplement produire une chaine de caractères de type `SVM_String` contenant de préférence au moins la liste des noyaux attachés au séquenceur avec leurs états.

- Un séquenceur commence par sa déclaration `SEQUENCER plugin.entry`, suivi par un bloc correspondant au contenu d'une structure C/C++ de type `sequencer_entry`.
- Les deux premières fonctions servent à créer `create` et à détruire `delete` la structure en C/C++ du séquenceur.
- La fonction de séquencement `current` permet d'incarner la politique de séquencement.
- Les fonctions d'attachement `attach` et de détachement `detach` permettent à un séquenceur respectivement de gérer un nouveau noyau ou d'évincer un noyau géré.
- La dernière fonction `print` permet de rendre une chaine de caractère décrivant l'état interne du séquenceur.

# Utilisation

## Utilisation basique

Pour pouvoir utiliser pleinement ce séquenceur, il faut ajouter une petite instruction pour attacher des noyaux supplémentaires :

    PLUGIN sequencer

    DEFINE

    SEQUENCER sequencer.instance
    %{
    	SVM_Kernel _main;
    	bool _interrupted;
    	SVM_Kernel _success;
    	SVM_Kernel _failure;
    %}
    create default:
    %{
    	object->_main = nullptr;
    	object->_interrupted = false;
    	object->_success = nullptr;
    	object->_failure = nullptr;
    %}
    delete default:
    %{
    	if(object->_main) VARIABLE_LOCAL(object->_main);
    	if(object->_success) VARIABLE_LOCAL(object->_success);
    	if(object->_failure) VARIABLE_LOCAL(object->_failure);
    %}
    current object:
    %{
    	if(object->_main and ::svm_kernel_is_runnable(svm,object->_main)) return object->_main;
    	if(not object->_interrupted)
    	{
    		if(object->_success and ::svm_kernel_is_runnable(svm,object->_success)) return object->_success;
    	}
    	else
    	{
    		if(object->_failure and ::svm_kernel_is_runnable(svm,object->_failure)) return object->_failure;
    	}
    	return nullptr;
    %}
    attach object:
    %{
    	if(argc==0)
    	{
    		if(object->_main) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_main = kernel;
    		return TRUE;
    	}
    	SVM_Boolean b = ARGV_VALUE(0,boolean);
    	if(b)
    	{
    		if(object->_success) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_success = kernel;
    	}
    	else
    	{
    		if(object->_failure) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_failure = kernel;
    	}
    	return TRUE;
    %}
    detach object:
    %{
    	if(kernel==object->_main)
    	{
    		VARIABLE_LOCAL(kernel); object->_main = nullptr;
    		object->_interrupted =::svm_kernel_get_state(svm,kernel)==INTERRUPTED;
    		return TRUE;
    	}
    	if(kernel==object->_success)
    	{
    		VARIABLE_LOCAL(kernel); object->_success = nullptr;
    		return TRUE;
    	}
    	if(kernel==object->_failure)
    	{
    		VARIABLE_LOCAL(kernel); object->_failure = nullptr;
    		return TRUE;
    	}
    	return FALSE;
    %}
    print object:
    %{
    	std::ostringstream oss;
    	oss << "Main kernel: ";
    	if(object->_main)
    	{
    		SVM_String m = ::svm_kernel_print(svm,object->_main);
    		oss << RAW_STRING(m);
    	}
    	oss << std::endl << "Main kernel " << (object->_interrupted?"":"not ") << "interrupted"
    	    << std::endl << "Success kernel: ";
    	if(object->_success)
    	{
    		SVM_String s = ::svm_kernel_print(svm,object->_success);
    		oss << RAW_STRING(s);
    	}
    	oss << std::endl << "Failure kernel: ";
    	if(object->_failure)
    	{
    		SVM_String f = ::svm_kernel_print(svm,object->_failure);
    		oss << RAW_STRING(f);
    	}
    	oss << std::endl;
    	return NEW_STRING(oss.str());
    %}

    INSTRUCTION sequencer.attach BLN [ LIB SYM ]
    %{
    	SVM_Value_Boolean b = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value c = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Kernel k = nullptr;
    	if(::svm_value_type_is_library(svm,c))
    	{
    		SVM_Code code = ::svm_value_library_get_code(svm,c);
    		k = ::svm_kernel_new_code(svm,FALSE,FALSE,FALSE,nullptr,code);
    		for(SVM_Value_Interruption *irq = ::svm_machine_list_interruption(svm) ; *irq ; ++irq)
    		{
    			::svm_processor_interruptionhandler_set_local_local(svm,k,*irq,0,FALSE);
    		}
    	}
    	else
    	{
    		k = ::svm_kernel_new_symbol(svm,FALSE,FALSE,FALSE,nullptr,c);
    		for(SVM_Value_Interruption *irq = ::svm_machine_list_interruption(svm) ; *irq ; ++irq)
    		{
    			::svm_processor_interruptionhandler_set_local_global(svm,k,*irq,c,FALSE);
    		}
    	}
    	SVM_Parameter *params = ::svm_parameter_array_new(svm,1);
    	params[0] = ::svm_parameter_value_new(svm,b);
    	if(not ::svm_process_kernel_attach(svm,CURRENT(process),k,1,params))
    	{
    		ERROR_INTERNAL(FAILURE,"Unable to attach kernel");
    	}
    %}

Générez et compilez l'extension.

Créez un fichier exécutable pour y placer un exemple d'application permettant d'illustrer le fonctionnement de ce séquenceur :

    #!/usr/bin/env svm
    LOG SERVER "localhost" "8081" QUIET
    PLUGIN "svmcom.so"
    PLUGIN "svmrun.so"
    LOCAL PLUGIN "svmpluginsequencer/libsvmsequencer.so"
    OPTION -i FLAG irq
    PROCESS "test"
    	CODE "main" INLINE
    		:memory LIB*2/l
    		:library "success" ":com.message \"success\"\n" -> (l/0)
    		:library "failure" ":com.message \"failure\"\n" -> (l/1)
    		:sequencer.attach TRUE @(l/0)
    		:sequencer.attach FALSE @(l/1)
    		:debug BREAK
    		:com.message "main"
    		:shutdown :unless @&irq TRUE
    		:run.interrupt FAILURE
    	END
    	MEMORY irq
    	SEQUENCER sequencer.instance
    END

Vous pouvez appeler cette application :

    ./sequencer.svm
    main
    success
    ./sequencer.svm -i
    main
    failure

- Dans le premier cas, le noyau "main" est exécuté et se termine normalement : le séquenceur lance le noyau "success".
- Dans le second cas, le noyau "main" est toujours exécuté, mais se termine sur une interruption. Cette fois, la machine virtuelle génère une trace pour ce noyau, puis lance le noyau "failure". Pour que cela fonctionne correctement, il est impératif que ce dernier noyau soit protégé contre les interruptions, car celle qui a interrompu le noyau "main" est transmise à ce nouveau noyau !

Pour faire simple, ce séquenceur réalise un `try ... catch` à un niveau élévé dans l'architecture de la machine virtuelle.

## Différentes politiques de séquencement

Le séquenceur présenté ici est relativement simple. En particulier, il ne peut accepter qu'au maximum trois noyaux.

D'autres séquenceurs peuvent avoir une politique de séquencement qui autorisent autant de noyaux que nécessaires, comme le séquenceur `run.stack` qui utilise une pile (LIFO) de noyaux pour enchainer des noyaux comme des appels de fonction, ou comme le séquenceur `inline.sequencer` qui utilise une liste de noyaux pour réaliser une séquence de noyaux avec un objectif commun.

Evidemment, dans l'absolu, n'importe quelle politique de séquencement peut être écrite pour satisfaire aux besoins particuliers d'une application.

## Altération d'un séquenceur

Il peut être parfois utile de modifier la structure d'un séquenceur en dehors des appels au séquenceur, via une instruction. Modifiez le code de l'extension :

    PLUGIN sequencer

    includes:
    %{
    #include <mutex>
    %}

    DEFINE

    SEQUENCER sequencer.instance
    %{
    	mutable std::mutex _mutex;
    	SVM_Kernel _main;
    	bool _interrupted;
    	SVM_Kernel _success;
    	SVM_Kernel _failure;
    %}
    create default:
    %{
    	std::lock_guard<std::mutex> protection(object->_mutex);
    	object->_main = nullptr;
    	object->_interrupted = false;
    	object->_success = nullptr;
    	object->_failure = nullptr;
    %}
    delete default:
    %{
    	std::lock_guard<std::mutex> protection(object->_mutex);
    	if(object->_main) VARIABLE_LOCAL(object->_main);
    	if(object->_success) VARIABLE_LOCAL(object->_success);
    	if(object->_failure) VARIABLE_LOCAL(object->_failure);
    %}
    current object:
    %{
    	std::lock_guard<std::mutex> protection(object->_mutex);
    	if(object->_main and ::svm_kernel_is_runnable(svm,object->_main)) return object->_main;
    	if(not object->_interrupted)
    	{
    		if(object->_success and ::svm_kernel_is_runnable(svm,object->_success)) return object->_success;
    	}
    	else
    	{
    		if(object->_failure and ::svm_kernel_is_runnable(svm,object->_failure)) return object->_failure;
    	}
    	return nullptr;
    %}
    attach object:
    %{
    	std::lock_guard<std::mutex> protection(object->_mutex);
    	if(argc==0)
    	{
    		if(object->_main) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_main = kernel;
    		return TRUE;
    	}
    	SVM_Boolean b = ARGV_VALUE(0,boolean);
    	if(b)
    	{
    		if(object->_success) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_success = kernel;
    	}
    	else
    	{
    		if(object->_failure) return FALSE;
    		VARIABLE_GLOBAL(kernel); object->_failure = kernel;
    	}
    	return TRUE;
    %}
    detach object:
    %{
    	std::lock_guard<std::mutex> protection(object->_mutex);
    	if(kernel==object->_main)
    	{
    		VARIABLE_LOCAL(kernel); object->_main = nullptr;
    		object->_interrupted =::svm_kernel_get_state(svm,kernel)==INTERRUPTED;
    		return TRUE;
    	}
    	if(kernel==object->_success)
    	{
    		VARIABLE_LOCAL(kernel); object->_success = nullptr;
    		return TRUE;
    	}
    	if(kernel==object->_failure)
    	{
    		VARIABLE_LOCAL(kernel); object->_failure = nullptr;
    		return TRUE;
    	}
    	return FALSE;
    %}
    print object:
    %{
    	std::lock_guard<std::mutex> protection(object->_mutex);
    	std::ostringstream oss;
    	oss << "Main kernel: ";
    	if(object->_main)
    	{
    		SVM_String m = ::svm_kernel_print(svm,object->_main);
    		oss << RAW_STRING(m);
    	}
    	oss << std::endl << "Main kernel " << (object->_interrupted?"":"not ") << "interrupted"
    	    << std::endl << "Success kernel: ";
    	if(object->_success)
    	{
    		SVM_String s = ::svm_kernel_print(svm,object->_success);
    		oss << RAW_STRING(s);
    	}
    	oss << std::endl << "Failure kernel: ";
    	if(object->_failure)
    	{
    		SVM_String f = ::svm_kernel_print(svm,object->_failure);
    		oss << RAW_STRING(f);
    	}
    	oss << std::endl;
    	return NEW_STRING(oss.str());
    %}

    INSTRUCTION sequencer.attach BLN [ LIB SYM ]
    %{
    	SVM_Value_Boolean b = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value c = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Kernel k = nullptr;
    	if(::svm_value_type_is_library(svm,c))
    	{
    		SVM_Code code = ::svm_value_library_get_code(svm,c);
    		k = ::svm_kernel_new_code(svm,FALSE,FALSE,FALSE,nullptr,code);
    		for(SVM_Value_Interruption *irq = ::svm_machine_list_interruption(svm) ; *irq ; ++irq)
    		{
    			::svm_processor_interruptionhandler_set_local_local(svm,k,*irq,0,FALSE);
    		}
    	}
    	else
    	{
    		k = ::svm_kernel_new_symbol(svm,FALSE,FALSE,FALSE,nullptr,c);
    		for(SVM_Value_Interruption *irq = ::svm_machine_list_interruption(svm) ; *irq ; ++irq)
    		{
    			::svm_processor_interruptionhandler_set_local_global(svm,k,*irq,c,FALSE);
    		}
    	}
    	SVM_Parameter *params = ::svm_parameter_array_new(svm,1);
    	params[0] = ::svm_parameter_value_new(svm,b);
    	if(not ::svm_process_kernel_attach(svm,CURRENT(process),k,1,params))
    	{
    		ERROR_INTERNAL(FAILURE,"Unable to attach kernel");
    	}
    %}

    INSTRUCTION sequencer.ignore BLN
    %{
    	SVM_Boolean b = ARGV_VALUE(0,boolean);
    	SVM_Value_PluginEntryPoint sn = ::svm_process_get_sequencer(svm,CURRENT(process));
    	if(not sn)
    	{
    		ERROR_INTERNAL(FAILURE,"Default sequencer attached to current process. sequencer.instance required.");
    	}
    	SVM_String snp = ::svm_value_pluginentrypoint_get_plugin(svm,sn);
    	SVM_String sne = ::svm_value_pluginentrypoint_get_entry(svm,sn);
    	if(not ((RAW_STRING(snp)=="sequencer") and (RAW_STRING(sne)=="instance")))
    	{
    		ERROR_INTERNAL(FAILURE,"Invalid plugin sequencer attacher to current process. sequencer.instance required.");
    	}
    	auto s = reinterpret_cast<sequencer_instance*>(::svm_process_sequencer_get_internal(svm,CURRENT(process)));
    	std::lock_guard<std::mutex> protection(s->_mutex);
    	if(b)
    	{
    		VARIABLE_LOCAL(s->_failure); s->_failure = nullptr;
    	else
    	{
    		VARIABLE_LOCAL(s->_success); s->_success = nullptr;
    	}
    %}

Vous pouvez remarquer :

- L'instruction veille à ce que le séquenceur du processus courant, récupéré avec la fonction `svm_process_get_sequencer`, soit bien de type `sequencer.instance`,
- La structure du séquenceur peut être récupéré via la fonction `svm_process_sequencer_get_internal`. Cette structure est de type C/C++ `sequencer_entry`,
- Comme l'instruction peut être appellée indépendamment des moments où le processus fait appel au séquenceur, il est fortement conseillé d'ajouter de la synchronisation sur la structure du séquenceur (ici, un `std::mutex`), et d'ajouter des barrières de synchronisation entre les fonctions du séquenceur et l'instruction.

Après regénération et recompilation de l'extension, ajoutez un appel à cette nouvelle instruction avec `TRUE` en paramètre dans l'application précédente, et constatez le changement de comportement :

    #!/usr/bin/env svm
    LOG SERVER "localhost" "8081" QUIET
    PLUGIN "svmcom.so"
    PLUGIN "svmrun.so"
    LOCAL PLUGIN "svmpluginsequencer/libsvmsequencer.so"
    OPTION -i FLAG irq
    PROCESS "test"
    	CODE "main" INLINE
    		:memory LIB*2/l
    		:library "success" ":com.message \"success\"\n" -> (l/0)
    		:library "failure" ":com.message \"failure\"\n" -> (l/1)
    		:sequencer.attach TRUE @(l/0)
    		:sequencer.attach FALSE @(l/1)
    		:debug BREAK
    		:com.message "main"
    		:sequencer.ignore TRUE
    		:shutdown :unless @&irq TRUE
    		:run.interrupt FAILURE
    	END
    	MEMORY irq
    	SEQUENCER sequencer.instance
    END

- Pour qu'un séquenceur soit pleinement utilisable, il faut pouvoir lui attacher des noyaux : le premier noyau d'un processus est attaché automatiquement, mais les autres doivent l'être grâce à des instructions dédiées.
- D'une manière générale, le choix d'un séquenceur pour un processus est une décision structurelle importante, et l'utilisateur avancé peut être à même d'écrire le sien grâce à une extension.
- Enfin, il est possible d'interférer directement avec la structure interne d'un séquenceur via la fonction de l'interface programmatique `svm_process_sequencer_get_internal`, souvent appelée après s'être assuré de la nature du séquenceur avec la fonction de l'interface programmatique `svm_process_get_sequencer`.

# Conclusion

Vous venez de voir comment créer et manipuler les séquenceurs depuis une extension.

Dans la majorité des processus, le séquenceur par défaut (ne supportant qu'un noyau) est suffisant. Cependant, changer de séquenceur est parfois requis pour le fonctionnement de certaines instructions.

Quand tous les séquenceurs déjà existants se révèlent insuffisants, il est possible d'en créer un grâce aux extensions.