# 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 processus 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 processus.svm_plugin en utilisant ce code :

    PLUGIN process

    DEFINE

# Obtenir un processus

## Création

Les processus peuvent être créés statiquement en utilisant le fichier d'application. Cela s'avère rapidement insuffisant lorsque les processus doivent être créés dynamiquement en fonction de données.

Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    	operator std::string() const { return "process"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print default: %{}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

Vous pouvez noter ici :

- un type a été défini pour manipuler un processus,
- un processus peut être créé avec les fonctions de l'interface programmatique `svm_process_new_code` et `svm_process_new_symbol`,
- en l'état, un processus créé ainsi ne peut pas être exécuté, car il faut l'attacher à un ordonnanceur pour cela.

Vous pouvez générer et compiler l'extension, même si elle n'est encore pas très utile.

## Récupération

Il est également possible de récupérer le processus courant. Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    	operator std::string() const { return "process"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print default: %{}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.current -> process.instance
    %{
    	SVM_Process p = ::svm_process_get_current(svm);
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

Vous pouvez générer et compiler l'extension, mais à nouveau, elle n'est pas exploitable en l'état.

- Un processus peut être créé grâce aux fonctions de l'interface programmatique `svm_process_new_code` et `svm_process_new_symbol`.
- Le processus courant peut être récupéré avec la fonction `svm_process_get_current`. L'appel à cette fonction peut même être remplacé par l'utilisation de la macro C/C++ `CURRENT(process)`.

# Verrouillage de processus

## Appartenance

Pour pouvoir modifier un processus, il est nécessaire que ce processus appartienne au processus courant : en effet, les processus sont des unités d'exécution du code totalement indépendantes et concurrentes et modifier un processus en cours d'exécution pourrait occasionner des plantages de la machine virtuelle.

Dans le cas où le code d'une instruction doit modifier un processus ou le noyau d'un processus, elle doit s'assurer que le processus appartient bien au processus courant.

Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    	operator std::string() const { return "process"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print default: %{}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.current -> process.instance
    %{
    	SVM_Process p = ::svm_process_get_current(svm);
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.owned process.instance -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
    	return NEW_VALUE(boolean,b);
    %}

Générez et compilez l'extension. Puis créez une application contenant ce code :

    #!/usr/bin/env svm
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmpluginprocess/libsvmprocess.so"
    PROCESS "process"
    	CODE "main" INLINE
    		:memory process.instance*2/p, BLN*2/b
    		:process.new "s" DEFAULT $"s" -> (p/0)
    		:process.current -> (p/1)
    		:process.owned @(p/0) -> (b/0)
    		:process.owned @(p/1) -> (b/1)
    		:com.message @(b/0) " " @(b/1)
    		:shutdown
    	:symbol s
    		:com.message "s"
    	END
    END

Puis lancez l'application :

    ./processus.svm
    FALSE TRUE

Vous pouvez constater que :

- le processus créé n'appartient pas au processus courant, et il faudra se l'approprier pour pouvoir agir dessus,
- le processus courant appartient toujours au processus courant. C'est d'ailleurs pour cela que jusqu'à présent, toutes les modifications faites sur les noyaux et processus dans les précédents didacticiels sont valides !

## Verrouillage

Pour qu'un autre processus appartienne au processus courant, il existe un seul moyen : verrouiller le processus distant depuis le processus courant.

Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    	operator std::string() const { return "process"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print default: %{}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.current -> process.instance
    %{
    	SVM_Process p = ::svm_process_get_current(svm);
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.owned process.instance -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
    	return NEW_VALUE(boolean,b);
    %}

    TYPE process.lock
    %{
    	SVM_Process_Lock _lock;
    	operator std::string() const { return "lock"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_lock);
    %}
    print default: %{}

    WAITING INSTRUCTION process.lock process.instance -> process.lock
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
    	VARIABLE_GLOBAL(l);
    	return NEW_PLUGIN(process,lock,new type_lock {l});
    %}

    WAITING INSTRUCTION process.critical_lock -> process.lock
    %{
    	SVM_Process_Lock l = ::svm_process_ownership_lock_critical(svm);
    	VARIABLE_GLOBAL(l);
    	return NEW_PLUGIN(process,lock,new type_lock {l});
    %}

Générez et compilez l'extension. Puis modifiez l'application avec ce code :

    #!/usr/bin/env svm
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmpluginprocess/libsvmprocess.so"
    PROCESS "process"
    	CODE "main" INLINE
    		:memory process.instance*2/p, BLN*2/b, process.lock/l
    		:process.new "s" DEFAULT $"s" -> (p/0)
    		:process.current -> (p/1)
    		:call owner P
    		:process.lock @(p/0) -> &l
    		:call owner P
    		[ ] -> l
    		:call owner P
    		:shutdown
    	:label owner
    		:process.owned @(p/0) -> (b/0)
    		:process.owned @(p/1) -> (b/1)
    		:com.message @(b/0) " " @(b/1)
    		:return
    	:symbol s
    		:com.message "s"
    	END
    END

Cette fois, l'exécution devient :

    ./processus.svm
    FALSE TRUE
    TRUE TRUE
    FALSE TRUE

Cette fois, entre le moment où le verrou depuis le processus distant est créé et où il est détruit, ce processus appartient au processus courant !

Notez la décorrelation entre l'appartenance et le verrouillage : en effet, la fonction qui teste l'appartenance ne demande pas de verrou en paramètre. Seule l'existence quelque part d'au moins un verrou suffit à garantir l'appartenance.

L'exemple ci-dessus utilise le verrou simple, qui permet de verrouiller un seul processus distant. Cependant, il existe un autre verrou dont l'utilisation est plutôt rare : le verrou critique qui verrouille tous les processus sauf le processus courant. Un tel verrouillage, même extrême, peut être utile lorsque l'exécution d'une instruction a un effet de bord pouvant être néfaste si d'autres processus s'exécutent en même temps.

Enfin, il existe une fonction de l'interface programmatique qui simplifie l'obtention de l'appartenance d'un processus distant : la fonction `svm_process_ownership_get_local` permettant d'obtenir l'appartenance localement à la fonction extension courante. Un exemple d'utilisation de cette fonction sera donné juste après dans ce didacticiel.

- La fonction `svm_process_ownership_lock` permet d'obtenir l'appartenance d'un processus distant au processus courant.
- La fonction `svm_process_ownership_lock_critical` permet d'obtenir l'appartenance de tous les autres processus au processus courant, pour des cas spécifiques.
- La fonction `svm_process_ownership_get_local` permet d'obtenir l'appartenance d'un autre processus durant la fonction courante.
- L'appartenance d'un processus au processus courant est acquise quand :
  - ce processus est le processus courant (sans verrou),
  - s'il existe au moins un verrou créé depuis le processus courant vers le processus distant. Il n'est pas nécessaire d'avoir accès au verrou pour vérifier l'appartenance du processus distant.

# Extension des fonctions de l'interface programmatique

Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    	operator std::string() const { return "process"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print default: %{}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.current -> process.instance
    %{
    	SVM_Process p = ::svm_process_get_current(svm);
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.owned process.instance -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
    	return NEW_VALUE(boolean,b);
    %}

    TYPE process.lock
    %{
    	SVM_Process_Lock _lock;
    	operator std::string() const { return "lock"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_lock);
    %}
    print default: %{}

    WAITING INSTRUCTION process.lock process.instance -> process.lock
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
    	VARIABLE_GLOBAL(l);
    	return NEW_PLUGIN(process,lock,new type_lock {l});
    %}

    INSTRUCTION process.write MUTABLE process.instance INT VALUE
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Address a = ARGV_VALUE(1,integer);
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
    	SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
    	SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
    	if(not pm)
    	{
    		ERROR_INTERNAL(MEMORY,"Invalid address");
    	}
    	::svm_memory_write_address(svm,k,a,v);
    %}

Générez et compilez l'extension. Puis modifiez l'application avec ce code :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmpluginprocess/libsvmprocess.so"
    PROCESS "process"
    	CODE "main" INLINE
    		:memory process.instance/p, process.lock/l
    		:process.new "s" DEFAULT $"s" -> &p
    		:process.lock @&p -> &l
    		:process.write @&p 0 "it works"
    		[ ] -> l
    		:process.write @&p 1 "failure"
    	:symbol s
    		:com.message "s"
    	END
    END

Lorsque cette application est lancée, une interruption intervient sur le second appel à l'instruction `:process.write` : il a cependant fonctionné sur le premier appel, lorsque le processus distant était verrouillé par le processus courant !

Cela permet de garantir qu'une donnée au sein de n'importe quel processus ne peut être accédée que par une seule instruction à la fois, pour éviter les modifications concurrentes génératrices d'erreurs aléatoires.

- Toute fonction de l'interface programmatique modifiant un noyau peut fonctionner sur tout noyau attaché à un processus appartenant au processus courant.
- Toute fonction de l'interface programmatique modifiant un processus peut fonctionner sur tout processus appartenant au processus courant.

# Opérations

## Attachement et détachement à un ordonnanceur

Pour qu'un processus soit exécuté, il est nécessaire de l'attacher à un ordonnanceur.

Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    	operator std::string() const { return "process"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print default: %{}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.current -> process.instance
    %{
    	SVM_Process p = ::svm_process_get_current(svm);
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.owned process.instance -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
    	return NEW_VALUE(boolean,b);
    %}

    TYPE process.lock
    %{
    	SVM_Process_Lock _lock;
    	operator std::string() const { return "lock"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_lock);
    %}
    print default: %{}

    WAITING INSTRUCTION process.lock process.instance -> process.lock
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
    	VARIABLE_GLOBAL(l);
    	return NEW_PLUGIN(process,lock,new type_lock {l});
    %}

    INSTRUCTION process.write MUTABLE process.instance INT VALUE
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Address a = ARGV_VALUE(1,integer);
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
    	SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
    	SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
    	if(not pm)
    	{
    		ERROR_INTERNAL(MEMORY,"Invalid address");
    	}
    	::svm_memory_write_address(svm,k,a,v);
    %}

    WAITING INSTRUCTION process.attach process.instance PEP:scheduler INT:parameter -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	if(not ::svm_process_ownership_get_local(svm,p->_process))
    	{
    		ERROR_INTERNAL(FAILURE,"Impossible to attach process");
    	}
    	SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
    	SVM_Boolean b = ::svm_scheduler_process_attach(svm,s,p->_process,i);
    	return NEW_VALUE(boolean,b);
    %}

    WAITING INSTRUCTION process.detach process.instance PEP:scheduler INT:parameter -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	if(not ::svm_process_ownership_get_local(svm,p->_process))
    	[46m{
    		ERROR_INTERNAL(FAILURE,"Impossible to detach process");
    	[46m}
    	SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
    	SVM_Boolean b = ::svm_scheduler_process_detach(svm,s,p->_process,i);
    	return NEW_VALUE(boolean,b);
    %}

Générez et compilez l'extension.

Notez la présence de la fonction `svm_process_ownership_get_local` qui ici permet d'obtenir l'appartenance du processus (en le verrouillant si nécessaire) sur l'ensemble de l'instruction : l'appartenance est perdue dès que l'instruction se termine.

Cela permet d'attacher le processus sans avoir de verrou au préalable :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    PLUGIN "svmrun.so"
    LOCAL PLUGIN "svmpluginprocess/libsvmprocess.so"
    PROCESS "process"
    	CODE "main" INLINE
    		:debug BREAK "process"
    		:memory process.instance/p, process.lock/l
    		:process.new "s" DEFAULT $"s" -> &p
    		:process.lock @&p -> &l
    		:process.write @&p 0 "it works"
    		[ ] -> l
    		:process.attach @&p run.parallel 0
    		:shutdown
    	:symbol s
    		:debug BREAK "s"
    		:com.message @&0
    	END
    END

Pour bien suivre le déroulement de cette application, il est conseillé de l'exécuter dans le débugueur et de suivre pas à pas son exécution.

## Suspension

Il est possible de demander à un processus de suspendre son exécution, même lorsqu'il n'appartient pas au processus courant. La demande de suspension est totalement asynchrone, la suspension étant effective une fois l'instruction en cours d'exécution terminée.

Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    	operator std::string() const { return "process"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print default: %{}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.current -> process.instance
    %{
    	SVM_Process p = ::svm_process_get_current(svm);
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.owned process.instance -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
    	return NEW_VALUE(boolean,b);
    %}

    TYPE process.lock
    %{
    	SVM_Process_Lock _lock;
    	operator std::string() const { return "lock"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_lock);
    %}
    print default: %{}

    WAITING INSTRUCTION process.lock process.instance -> process.lock
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
    	VARIABLE_GLOBAL(l);
    	return NEW_PLUGIN(process,lock,new type_lock {l});
    %}

    INSTRUCTION process.write MUTABLE process.instance INT VALUE
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Address a = ARGV_VALUE(1,integer);
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
    	SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
    	SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
    	if(not pm)
    	{
    		ERROR_INTERNAL(MEMORY,"Invalid address");
    	}
    	::svm_memory_write_address(svm,k,a,v);
    %}

    WAITING INSTRUCTION process.attach process.instance PEP:scheduler INT:parameter -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	if(not ::svm_process_ownership_get_local(svm,p->_process))
    	{
    		ERROR_INTERNAL(FAILURE,"Impossible to attach process");
    	}
    	SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
    	SVM_Boolean b = ::svm_scheduler_process_attach(svm,s,p->_process,i);
    	return NEW_VALUE(boolean,b);
    %}

    WAITING INSTRUCTION process.detach process.instance PEP:scheduler INT:parameter -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	if(not ::svm_process_ownership_get_local(svm,p->_process))
    	{
    		ERROR_INTERNAL(FAILURE,"Impossible to detach process");
    	}
    	SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
    	SVM_Boolean b = ::svm_scheduler_process_detach(svm,s,p->_process,i);
    	return NEW_VALUE(boolean,b);
    %}

    INSTRUCTION process.suspend process.instance
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	::svm_process_suspend(svm,p->_process);
    %}

Générez et compilez l'extension.

Vous pouvez écrire une petite application utilisant cette nouvelle instruction, mais l'effet de cette instruction avec les ordonnanceurs disponibles est peu visible. En effet, une fois le processus suspendu, il est généralement à la charge de son ordonnanceur de relancer son exécution et les ordonnanceurs fournis de base avec la machine virtuelle vont relancer immédiatement les processus suspendus.

## Terminaison

De même, il est possible de demander la terminaison d'un processus.

Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    	operator std::string() const { return "process"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print default: %{}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.current -> process.instance
    %{
    	SVM_Process p = ::svm_process_get_current(svm);
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.owned process.instance -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
    	return NEW_VALUE(boolean,b);
    %}

    TYPE process.lock
    %{
    	SVM_Process_Lock _lock;
    	operator std::string() const { return "lock"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_lock);
    %}
    print default: %{}

    WAITING INSTRUCTION process.lock process.instance -> process.lock
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
    	VARIABLE_GLOBAL(l);
    	return NEW_PLUGIN(process,lock,new type_lock {l});
    %}

    INSTRUCTION process.write MUTABLE process.instance INT VALUE
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Address a = ARGV_VALUE(1,integer);
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
    	SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
    	SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
    	if(not pm)
    	{
    		ERROR_INTERNAL(MEMORY,"Invalid address");
    	}
    	::svm_memory_write_address(svm,k,a,v);
    %}

    WAITING INSTRUCTION process.attach process.instance PEP:scheduler INT:parameter -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	if(not ::svm_process_ownership_get_local(svm,p->_process))
    	{
    		ERROR_INTERNAL(FAILURE,"Impossible to attach process");
    	}
    	SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
    	SVM_Boolean b = ::svm_scheduler_process_attach(svm,s,p->_process,i);
    	return NEW_VALUE(boolean,b);
    %}

    WAITING INSTRUCTION process.detach process.instance PEP:scheduler INT:parameter -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	if(not ::svm_process_ownership_get_local(svm,p->_process))
    	{
    		ERROR_INTERNAL(FAILURE,"Impossible to detach process");
    	}
    	SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
    	SVM_Boolean b = ::svm_scheduler_process_detach(svm,s,p->_process,i);
    	return NEW_VALUE(boolean,b);
    %}

    INSTRUCTION process.suspend process.instance
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	::svm_process_suspend(svm,p->_process);
    %}

    INSTRUCTION process.terminate process.instance
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	::svm_process_terminate(svm,p->_process);
    %}

Générez et compilez l'extension.

Vous pouvez écrire une petite application utilisant cette nouvelle instruction. Cette fois, l'effet est plus visible, car le processus ciblé par cette nouvelle instruction va arrêter son exécution dès l'instruction courante terminée !

## Introspection

Il est possible de récupérer certaines informations sur un processus.

Modifiez le code de l'extension :

    PLUGIN process

    DEFINE

    TYPE process.instance
    %{
    	SVM_Process _process;
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_process);
    %}
    print object:
    %{
    	return ::svm_process_print(svm,object->_process);
    %}

    INSTRUCTION process.new STR:name [ 'DEFAULT' PEP ]:sequencer [ LIB SYM ] -> process.instance
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_PluginEntryPoint s = nullptr;
    	if(::svm_parameter_type_is_value(svm,argv[1]))
    	{
    		s = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Process p = nullptr;
    	if(::svm_value_type_is_library(svm,v))
    	{
    		SVM_Code c = ::svm_value_library_get_code(svm,v);
    		p = ::svm_process_new_code(svm,n,s,FALSE,c,TRUE,FALSE,FALSE,nullptr);
    	}
    	else
    	{
    		p = ::svm_process_new_symbol(svm,n,s,FALSE,v,TRUE,FALSE,FALSE,nullptr);
    	}
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.current -> process.instance
    %{
    	SVM_Process p = ::svm_process_get_current(svm);
    	VARIABLE_GLOBAL(p);
    	return NEW_PLUGIN(process,instance,new type_instance {p});
    %}

    INSTRUCTION process.owned process.instance -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Boolean b = ::svm_process_ownership_check(svm,p->_process);
    	return NEW_VALUE(boolean,b);
    %}

    TYPE process.lock
    %{
    	SVM_Process_Lock _lock;
    	operator std::string() const { return "lock"; }
    %}
    delete default:
    %{
    	VARIABLE_LOCAL(object->_lock);
    %}
    print default: %{}

    WAITING INSTRUCTION process.lock process.instance -> process.lock
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Process_Lock l = ::svm_process_ownership_lock(svm,p->_process);
    	VARIABLE_GLOBAL(l);
    	return NEW_PLUGIN(process,lock,new type_lock {l});
    %}

    INSTRUCTION process.write MUTABLE process.instance INT VALUE
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Address a = ARGV_VALUE(1,integer);
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,1);
    	SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
    	SVM_Value_Pointer pm = ::svm_memory_allocate_address(svm,k,z,a);
    	if(not pm)
    	{
    		ERROR_INTERNAL(MEMORY,"Invalid address");
    	}
    	::svm_memory_write_address(svm,k,a,v);
    %}

    WAITING INSTRUCTION process.attach process.instance PEP:scheduler INT:parameter -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	if(not ::svm_process_ownership_get_local(svm,p->_process))
    	{
    		ERROR_INTERNAL(FAILURE,"Impossible to attach process");
    	}
    	SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
    	SVM_Boolean b = ::svm_scheduler_process_attach(svm,s,p->_process,i);
    	return NEW_VALUE(boolean,b);
    %}

    WAITING INSTRUCTION process.detach process.instance PEP:scheduler INT:parameter -> BLN
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	if(not ::svm_process_ownership_get_local(svm,p->_process))
    	{
    		ERROR_INTERNAL(FAILURE,"Impossible to detach process");
    	}
    	SVM_Value_PluginEntryPoint sn = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[2]);
    	SVM_Scheduler s = ::svm_scheduler_get(svm,sn);
    	SVM_Boolean b = ::svm_scheduler_process_detach(svm,s,p->_process,i);
    	return NEW_VALUE(boolean,b);
    %}

    INSTRUCTION process.suspend process.instance
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	::svm_process_suspend(svm,p->_process);
    %}

    INSTRUCTION process.terminate process.instance
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	::svm_process_terminate(svm,p->_process);
    %}

    INSTRUCTION process.name process.instance -> STR
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_String n = ::svm_process_get_name(svm,p->_process);
    	return NEW_VALUE(string,n);
    %}

    INSTRUCTION process.state process.instance -> INT
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Process_State s = ::svm_process_get_state(svm,p->_process);
    	return NEW_VALUE(integer,s);
    %}

    INSTRUCTION process.irq process.instance -> IRQ ?
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Value_Interruption i = ::svm_process_get_interruption(svm,p->_process);
    	if(not i)
    	{
    		return NEW_NULL_VALUE(interruption);
    	}
    	return i;
    %}

    SYSTEM INSTRUCTION process.coredump process.instance -> STR
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_String n = ::svm_process_get_coredump(svm,p->_process);
    	return NEW_VALUE(string,n);
    %}

    INSTRUCTION process.kernel process.instance -> STR
    %{
    	auto p = ARGV_PLUGIN(0,process,instance);
    	SVM_Kernel k = ::svm_process_kernel_get_current(svm,p->_process);
    	SVM_String s = ::svm_kernel_print(svm,k);
    	return NEW_VALUE(string,s);
    %}

Générez et compilez l'extension (notez que la fonction d'affichage du type `process.instance` a également été modifiée), puis écrivez une petite application qui crée un processus, puis appelle ces fonctions sur le processus créé.

- Pour exécuter un processus, il est nécessaire de l'attacher à un ordonnanceur avec la fonction `svm_scheduler_process_attach`. L'opération inverse se réalise avec la fonction `svm_scheduler_process_detach`.
- Les fonctions `svm_process_suspend` et `svm_process_terminate` permettent de demander respectivement la suspension (une pause) et la terminaison (l'arrêt) du processus ciblé.
- Les fonctions d'introspection d'un processus sont :
  - `svm_process_get_name` pour récupérer le nom du processus,
  - `svm_process_get_state` pour récupérer l'état d'exécution du processus,
  - `svm_process_get_interruption` pour récupérer l'interruption qui a arrêté le processus lorsque son état est égal à `INTERRUPTED`,
  - `svm_process_get_coredump` pour récupérer une représentation textuelle de l'ensemble du contenu du processus (à utiliser de préférence dans une instruction système),
  - `svm_process_print` pour récupérer une représentation textuelle de l'état d'exécution du processus,
  - `svm_process_kernel_get_current` pour récupérer le noyau actuellement en cours d'exécution dans le processus.

# Conclusion

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

La maîtrise de la manipulation des processus est une compétence clef pour développer des applications de taille industrielle. En effet, toute architecture matérielle actuelle comporte plusieurs cœurs de calcul, et il serait vraiment dommage de ne pas en profiter lorsque cela peut être utile. Or, c'est au travers des processus que la machine hôte pourra être exploitée à ses pleines capacités.

Pour des applications plus modestes, il est utile de savoir comment créer un processus et l'attacher à un ordonnanceur. Le reste relève d'une utilisation avancée des processus, et peut être mis de côté au début, pour être repris plus tard lorsque la nécessité fait foi.