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 :

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.

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 :

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.

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.

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))
	{
		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);
%}

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éé.

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.