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 les ordonnanceurs de la machine virtuelle.
Le temps de lecture de ce didacticiel est estimé à 35 minutes.
Pour commencer, créez le canevas de l'extension dans le fichier ordonnanceurs.svm_plugin en utilisant ce code :
PLUGIN scheduler
DEFINE
Un ordonnanceur a une mission très simple dans la machine virtuelle : il orchestre l'exécution des processus qu'il a à sa charge selon une politique d'ordonnancement liée à son implémentation.
Comme pour les séquenceurs, le premier élément d'un ordonnanceur est sa déclaration suivie d'une structure pouvant contenir les données liées à son fonctionnement. Ici, la struct C/C++ générée a pour type le nom formé de la chaine fixe "scheduler_" suivie du nom de l'entrée de l'ordonnanceur (ce qui donne ici le type scheduler_instance).
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
%}
DEFINE
SCHEDULER scheduler.instance
%{
std::list<SVM_Process> _processes;
%}
Ensuite, deux fonctions pour créer et détruire la structure de l'ordonnanceur doivent être définies.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
%}
DEFINE
SCHEDULER scheduler.instance
%{
std::list<SVM_Process> _processes;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p);
}
%}
Sur ces deux fonctions, les options default permettent de générer le code de base de construction et de destruction de la structure. De plus, dans la fonction de destruction, cela définit la variable magique object utilisable pour accéder directement à la structure de l'ordonnanceur.
Chacune de ces fonctions est appelée une seule fois par exécution de la machine virtuelle :
La fonction suivante est le cœur de l'ordonnanceur : il s'agit de la fonction qui incarne la politique d'ordonnancement.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
%}
DEFINE
SCHEDULER scheduler.instance
%{
std::list<SVM_Process> _processes;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p);
}
%}
schedule object:
%{
if(state==SUSPENDED)
{
::svm_process_run__raw(svm,process,0);
}
return 0;
%}
Cette fonction, ici très simple (elle réalise l'ordonnancement le plus facile, celui qui exécute tous les processus en même temps), est pourtant plutôt délicate à écrire :
process qui indique quel processus a changé d'état,state qui indique quel est le nouvel état de ce processus ;Pour pouvoir gérer des processus, ceux-ci doivent être attachés à l'ordonnanceur. Réciproquement, lorsqu'un processus ne doit plus être ordonnancé, ou avant d'être attaché à un autre ordonnanceur, il doit être détaché.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
#include <algorithm>
%}
DEFINE
SCHEDULER scheduler.instance
%{
std::list<SVM_Process> _processes;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p);
}
%}
schedule object:
%{
if(state==SUSPENDED)
{
::svm_process_run__raw(svm,process,0);
}
return 0;
%}
attach object:
%{
VARIABLE_GLOBAL(process);
object->_processes.push_back(process);
return TRUE;
%}
detach object:
%{
auto it = std::find(object->_processes.begin(),object->_processes.end(),process);
if(it==object->_processes.end()) return FALSE;
VARIABLE_LOCAL(process);
object->_processes.erase(it);
return TRUE;
%}
Ces fonctions récupèrent le processus et le placent ou l'enlève de l'ordonnanceur. Dans les deux cas, elles retournent TRUE lorsque l'opération réussit, et FALSE sinon.
Notez que ces fonctions peuvent recevoir des paramètres via les variables magiques argc et argv. Par défaut, ces variables valent 0 et nullptr pour indiquer l'absence de parametres, et toutes les autres valeurs peuvent modifier comment l'ordonnanceur va attacher ou détacher les processus dans ses structures internes.
La dernière fonction obligatoire sert à l'investigation, et donne sous forme textuelle une représentation de l'état de l'ordonnanceur, ainsi que les processus gérés.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
#include <algorithm>
%}
DEFINE
SCHEDULER scheduler.instance
%{
std::list<SVM_Process> _processes;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p);
}
%}
schedule object:
%{
if(state==SUSPENDED)
{
::svm_process_run__raw(svm,process,0);
}
return 0;
%}
attach object:
%{
VARIABLE_GLOBAL(process);
object->_processes.push_back(process);
return TRUE;
%}
detach object:
%{
auto it = std::find(object->_processes.begin(),object->_processes.end(),process);
if(it==object->_processes.end()) return FALSE;
VARIABLE_LOCAL(process);
object->_processes.erase(it);
return TRUE;
%}
print object:
%{
std::ostringstream oss;
for(const auto& p: object->_processes)
{
SVM_String s = ::svm_process_print(svm,p);
oss << RAW_STRING(s) << std::endl;
}
return NEW_STRING(oss.str());
%}
Une fois cette fonction ajoutée, l'ordonnanceur est complet.
Générez l'extension et compilez la.
Ecrivez une petite application déclarant deux ou trois processus attachés en utilisant la directive SCHEDULER scheduler.instance, et exécutez la : tous les processus vont s'exécuter en même temps, sans relation de dépendance.
Il est même recommandé de lancer cette application dans le débugueur, et de regarder les interactions entre l'ordonnanceur et les processus, dans la fenêtre d'événements. Cette fenêtre est un outil puissant pour mettre au point un ordonnanceur.
svm_process_run, qui (re)lance l'exécution d'un processus.La réactivité aux changements d'état des processus n'est pas le seul outil à la portée des ordonnanceurs, et nous allons voir ici un premier outil.
Pour illustrer correctement le concept de notifications, il nous faut adopter une autre politique d'ordonnancement : un seul processus en exécution, les autres en attente.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
#include <algorithm>
%}
DEFINE
SCHEDULER scheduler.instance
%{
std::list<std::pair<SVM_Process,SVM_Process_State> > _processes;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p.first);
}
%}
schedule object:
%{
SVM_Process_State ancien_etat;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
ancien_etat = it->second;
it->second = state;
if((ancien_etat==RUNNING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_back(p);
}
if((ancien_etat==WAITING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_front(p);
}
break;
}
}
bool en_cours = false;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
en_cours = true;
}
if(not en_cours)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==SUSPENDED)
{
::svm_process_run__raw(svm,it->first,0);
break;
}
}
}
return 0;
%}
attach object:
%{
VARIABLE_GLOBAL(process);
object->_processes.push_back(std::make_pair(process,SUSPENDED));
return TRUE;
%}
detach object:
%{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
VARIABLE_LOCAL(process);
object->_processes.erase(it);
return TRUE;
}
}
return FALSE;
%}
print object:
%{
std::ostringstream oss;
for(const auto& p: object->_processes)
{
SVM_String s = ::svm_process_print(svm,p.first);
oss << RAW_STRING(s) << std::endl;
}
return NEW_STRING(oss.str());
%}
Mais ainsi, l'ordonnanceur n'est guère utilisable : un seul processus va s'exécuter, jusqu'à ce qu'il se termine ou se suspende de lui même. Dans certaines circonstances, c'est souhaitable, mais ce n'est pas notre but ici : nous voulons changer le processus "de temps en temps".
Une première manière de procéder consiste à définir une notification qui va suspendre le processus courant, et fournir une instruction envoyant cette notification.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
#include <algorithm>
%}
DEFINE
SCHEDULER scheduler.instance
%{
std::list<std::pair<SVM_Process,SVM_Process_State> > _processes;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p.first);
}
%}
schedule object:
%{
SVM_Process_State ancien_etat;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
ancien_etat = it->second;
it->second = state;
if((ancien_etat==RUNNING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_back(p);
}
if((ancien_etat==WAITING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_front(p);
}
break;
}
}
bool en_cours = false;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
en_cours = true;
}
if(not en_cours)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==SUSPENDED)
{
::svm_process_run__raw(svm,it->first,0);
break;
}
}
}
return 0;
%}
notification object:
%{
if(argc==0)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
{
::svm_process_suspend(svm,it->first);
break;
}
}
}
return TRUE;
%}
attach object:
%{
VARIABLE_GLOBAL(process);
object->_processes.push_back(std::make_pair(process,SUSPENDED));
return TRUE;
%}
detach object:
%{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
VARIABLE_LOCAL(process);
object->_processes.erase(it);
return TRUE;
}
}
return FALSE;
%}
print object:
%{
std::ostringstream oss;
for(const auto& p: object->_processes)
{
SVM_String s = ::svm_process_print(svm,p.first);
oss << RAW_STRING(s) << std::endl;
}
return NEW_STRING(oss.str());
%}
INSTRUCTION scheduler.next
%{
SVM_Scheduler s = ::svm_scheduler_get(svm,CONST_PEP(scheduler,instance));
::svm_scheduler_notify(svm,s,0,nullptr);
%}
Vous pouvez noter que dans la fonction de notification de l'ordonnanceur, les variables magiques argc et argv contiennent les paramètres donnés en arguments de la fonction svm_scheduler_notify.
Générez puis compilez l'extension. Puis créez une application avec ce code :
#!/usr/bin/env svm
LOG
PLUGIN "svmrun.so"
LOCAL PLUGIN "svmpluginscheduler/libsvmscheduler.so"
ARGUMENT INT repetition
ARGUMENT INT interval
ARGUMENT INT loop
PROCESS "controler"
CODE "main" INLINE
:shutdown :when @&repetition IN &0*1
:memory INT/i
0 -> &i
:label loop
:run.sleep HARD @&interval
:scheduler.next
:shift &i
:goto loop :when @&i IN &0*@&repetition
END
MEMORY repetition interval
END
PROCESS "1"
CODE "main" INLINE
:memory INT/i
0 -> &i
:label loop
:run.sleep HARD 1
:run.trace @&i
:shift &i
:goto loop :when @&i IN &0*@&loop
END
MEMORY loop
SCHEDULER scheduler.instance
END
PROCESS "2"
CODE "main" INLINE
:memory INT/i
0 -> &i
:label loop
:run.sleep HARD 1
:run.trace @&i
:shift &i
:goto loop :when @&i IN &0*@&loop
END
MEMORY loop
SCHEDULER scheduler.instance
END
PROCESS "3"
CODE "main" INLINE
:memory INT/i
0 -> &i
:label loop
:run.sleep HARD 1
:run.trace @&i
:shift &i
:goto loop :when @&i IN &0*@&loop
END
MEMORY loop
SCHEDULER scheduler.instance
END
Lancez l'application avec des valeurs d'arguments variées, et observez le résultat :
| repetition | interval | loop | Résultat obtenu sur les processus 1, 2 et 3 |
|---|---|---|---|
| 0 | 5 | 30 | Chaque processus s'exécute entièrement avant de donner la main au suivant. |
| 20 | 5 | 30 | Chaque processus exécute 4 ou 5 boucles avant de passer la main au processus suivant. |
| 9 | 3 | 30 | En moyenne, les processus commencent par exécuter une ou deux boucles en se passant la main à tour de rôle, puis au bout d'un moment chaque processus s'exécute jusqu'à sa terminaison avant de passer la main au suivant. |
| 5 | 20 | 30 | Chaque processus va exécuter une bonne partie de ses boucles avant de passer la main au suivant. Puis, sur la deuxième rotation, tous les processus vont s'arrer avant que la notification suivante arrive. |
Il est fortement recommandé de suivre le déroulement de l'exécution de cette application dans le débugueur, en se focalisant sur les fenêtres :
notification sur l'ordonnanceur, où les variables magiques argc et argv contiennent les paramètres de la notification. La fonction doit retourner TRUE lorsque la notification a été processée, et FALSE dans le cas contraire.svm_scheduler_notify en y passant les paramètres de la notification.En plus des notifications, les ordonnanceurs ont accès à une horloge intégrée. Pour être plus précis, il s'agit pas d'une horloge monotonique (régulière), mais plutôt d'une échéance programmable qui se déclenche lorsqu'aucune notification ou changement d'état n'est intervenu dans le temps imparti.
Cela permet à un ordonnanceur de rester réactif en cas de changement d'état tout en ayant la possibilité d'agir si rien ne s'est produit dans un temps imparti.
Nous allons ici ajouter la capacité à notre dernier ordonnanceur de réagir à une horloge, tout en laissant la possibilité de gérer le changement de processus via une notification.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
#include <algorithm>
%}
DEFINE
SCHEDULER scheduler.instance
%{
scheduler_instance()
:_timer(0) {}
std::list<std::pair<SVM_Process,SVM_Process_State> > _processes;
unsigned long int _timer;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p.first);
}
%}
schedule object:
%{
SVM_Process_State ancien_etat;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
ancien_etat = it->second;
it->second = state;
if((ancien_etat==RUNNING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_back(p);
}
if((ancien_etat==WAITING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_front(p);
}
break;
}
}
bool en_cours = false;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
en_cours = true;
}
if(not en_cours)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==SUSPENDED)
{
::svm_process_run__raw(svm,it->first,0);
break;
}
}
}
return 0;
%}
notification object:
%{
if(argc==0)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
{
::svm_process_suspend(svm,it->first);
break;
}
}
return TRUE;
}
long long int timer = ARGV_VALUE(0,integer);
if(timer<0) return FALSE;
object->_timer = timer;
return TRUE;
%}
attach object:
%{
VARIABLE_GLOBAL(process);
object->_processes.push_back(std::make_pair(process,SUSPENDED));
return TRUE;
%}
detach object:
%{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
VARIABLE_LOCAL(process);
object->_processes.erase(it);
return TRUE;
}
}
return FALSE;
%}
print object:
%{
std::ostringstream oss;
for(const auto& p: object->_processes)
{
SVM_String s = ::svm_process_print(svm,p.first);
oss << RAW_STRING(s) << std::endl;
}
return NEW_STRING(oss.str());
%}
INSTRUCTION scheduler.next
%{
SVM_Scheduler s = ::svm_scheduler_get(svm,CONST_PEP(scheduler,instance));
::svm_scheduler_notify(svm,s,0,nullptr);
%}
INSTRUCTION scheduler.timer INT
%{
SVM_Scheduler s = ::svm_scheduler_get(svm,CONST_PEP(scheduler,instance));
::svm_scheduler_notify(svm,s,argc,argv);
%}
Concrètement, une valeur entière a été ajoutée à la structure de l'ordonnanceur, et le nécessaire pour configurer cette valeur au travers de notifications.
Il s'agit maintenant d'activer l'horloge, et de réagir à l'expiration du délai.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
#include <algorithm>
%}
DEFINE
SCHEDULER scheduler.instance
%{
scheduler_instance()
:_timer(0) {}
void suspend(const void *svm)
{
for(auto it=_processes.begin() ; it!=_processes.end() ; ++it)
{
if(it->second==RUNNING)
{
::svm_process_suspend(svm,it->first);
break;
}
}
}
std::list<std::pair<SVM_Process,SVM_Process_State> > _processes;
unsigned long int _timer;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p.first);
}
%}
schedule object:
%{
SVM_Process_State ancien_etat;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
ancien_etat = it->second;
it->second = state;
if((ancien_etat==RUNNING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_back(p);
}
if((ancien_etat==WAITING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_front(p);
}
break;
}
}
bool en_cours = false;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
en_cours = true;
}
if(not en_cours)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==SUSPENDED)
{
::svm_process_run__raw(svm,it->first,0);
break;
}
}
}
return object->_timer;
%}
timer object:
%{
object->suspend(svm);
return object->_timer;
%}
notification object:
%{
if(argc==0)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
{
::svm_process_suspend(svm,it->first);
break;
}
}
return TRUE;
}
long long int timer = ARGV_VALUE(0,integer);
if(timer<0) return FALSE;
object->_timer = timer;
return TRUE;
%}
attach object:
%{
VARIABLE_GLOBAL(process);
object->_processes.push_back(std::make_pair(process,SUSPENDED));
return TRUE;
%}
detach object:
%{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
VARIABLE_LOCAL(process);
object->_processes.erase(it);
return TRUE;
}
}
return FALSE;
%}
print object:
%{
std::ostringstream oss;
for(const auto& p: object->_processes)
{
SVM_String s = ::svm_process_print(svm,p.first);
oss << RAW_STRING(s) << std::endl;
}
return NEW_STRING(oss.str());
%}
INSTRUCTION scheduler.next
%{
SVM_Scheduler s = ::svm_scheduler_get(svm,CONST_PEP(scheduler,instance));
::svm_scheduler_notify(svm,s,0,nullptr);
%}
INSTRUCTION scheduler.timer INT
%{
SVM_Scheduler s = ::svm_scheduler_get(svm,CONST_PEP(scheduler,instance));
::svm_scheduler_notify(svm,s,argc,argv);
%}
Maintenant, la valeur de retour de la fonction d'ordonnancement n'est plus 0, mais correspond au temps d'attente avant déclenchement de l'horloge : ce temps d'attente est exprimé en millisecondes, ou désactive l'horloge quand le temps d'attente est mis à 0.
De plus, une nouvelle fonction a été ajoutée à l'ordonnanceur. Cette fonction sera invoquée lorsque le délai d'attente expire sans qu'autre événement vienne réveiller l'ordonnanceur.
Générez puis compilez l'extension. Puis modifiez l'application avec ce code :
#!/usr/bin/env svm
LOG
PLUGIN "svmrun.so"
LOCAL PLUGIN "svmpluginscheduler/libsvmscheduler.so"
ARGUMENT INT repetition
ARGUMENT INT interval
ARGUMENT INT loop
ARGUMENT INT timer
PROCESS "controler"
CODE "main" INLINE
:shutdown :when @&repetition IN &0*1
:memory INT/i
0 -> &i
:label loop
:run.sleep HARD @&interval
:scheduler.next
:shift &i
:goto loop :when @&i IN &0*@&repetition
:scheduler.timer 3000
:run.trace "Timer ON"
:run.sleep HARD @&timer
:run.trace "Timer OFF"
:scheduler.timer 0
END
MEMORY repetition interval timer
END
PROCESS "1"
CODE "main" INLINE
:memory INT/i
0 -> &i
:label loop
:run.sleep HARD 1
:run.trace @&i
:shift &i
:goto loop :when @&i IN &0*@&loop
END
MEMORY loop
SCHEDULER scheduler.instance
END
PROCESS "2"
CODE "main" INLINE
:memory INT/i
0 -> &i
:label loop
:run.sleep HARD 1
:run.trace @&i
:shift &i
:goto loop :when @&i IN &0*@&loop
END
MEMORY loop
SCHEDULER scheduler.instance
END
PROCESS "3"
CODE "main" INLINE
:memory INT/i
0 -> &i
:label loop
:run.sleep HARD 1
:run.trace @&i
:shift &i
:goto loop :when @&i IN &0*@&loop
END
MEMORY loop
SCHEDULER scheduler.instance
END
Vous pouvez lancer l'application ainsi :
./scheduler.svm 5 5 30 20
Vous constaterez que :
Timer ON et Timer OFF, le processus de contrôle ne fait qu'attendre. Cependant, les processus sont bien ordonnancés tour à tour, grâce à l'horloge : il n'est plus nécessaire de provoquer un changement de processus, l'ordonnanceur le fait désormais tout seul régulièrement.Timer OFF, l'ordonnancement devient très simple : l'horloge étant désactivée et l'ordonnanceur ne recevant plus de notifications pour changer de processus, chaque processus termine son exécution avant de passer la main au suivant.La première opération disponible est la possibilité de récupérer un ordonnanceur à partir de son nom. La fonction de l'interface programmatique svm_scheduler_get permet cette opération.
Il n'est pas nécessaire de modifier le code de l'extension, car cette fonction est déjà utilisée dans les exemples précédents.
La seconde opération permet de récupérer une version textuelle de l'état de l'ordonnanceur.
Modifiez le code de l'extension :
PLUGIN scheduler
includes:
%{
#include <list>
#include <algorithm>
%}
DEFINE
SCHEDULER scheduler.instance
%{
scheduler_instance()
:_timer(0) {}
void suspend(const void *svm)
{
for(auto it=_processes.begin() ; it!=_processes.end() ; ++it)
{
if(it->second==RUNNING)
{
::svm_process_suspend(svm,it->first);
break;
}
}
}
std::list<std::pair<SVM_Process,SVM_Process_State> > _processes;
unsigned long int _timer;
%}
create default: %{}
delete default:
%{
for(auto& p:object->_processes)
{
VARIABLE_LOCAL(p.first);
}
%}
schedule object:
%{
SVM_Process_State ancien_etat;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
ancien_etat = it->second;
it->second = state;
if((ancien_etat==RUNNING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_back(p);
}
if((ancien_etat==WAITING) and (state==SUSPENDED))
{
auto p = *it;
object->_processes.erase(it);
object->_processes.push_front(p);
}
break;
}
}
bool en_cours = false;
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
en_cours = true;
}
if(not en_cours)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==SUSPENDED)
{
::svm_process_run__raw(svm,it->first,0);
break;
}
}
}
return object->_timer;
%}
timer object:
%{
object->suspend(svm);
return object->_timer;
%}
notification object:
%{
if(argc==0)
{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->second==RUNNING)
{
::svm_process_suspend(svm,it->first);
break;
}
}
return TRUE;
}
long long int timer = ARGV_VALUE(0,integer);
if(timer<0) return FALSE;
object->_timer = timer;
return TRUE;
%}
attach object:
%{
VARIABLE_GLOBAL(process);
object->_processes.push_back(std::make_pair(process,SUSPENDED));
return TRUE;
%}
detach object:
%{
for(auto it=object->_processes.begin() ; it!=object->_processes.end() ; ++it)
{
if(it->first==process)
{
VARIABLE_LOCAL(process);
object->_processes.erase(it);
return TRUE;
}
}
return FALSE;
%}
print object:
%{
std::ostringstream oss;
for(const auto& p: object->_processes)
{
SVM_String s = ::svm_process_print(svm,p.first);
oss << RAW_STRING(s) << std::endl;
}
return NEW_STRING(oss.str());
%}
INSTRUCTION scheduler.next
%{
SVM_Scheduler s = ::svm_scheduler_get(svm,CONST_PEP(scheduler,instance));
::svm_scheduler_notify(svm,s,0,nullptr);
%}
INSTRUCTION scheduler.timer INT
%{
SVM_Scheduler s = ::svm_scheduler_get(svm,CONST_PEP(scheduler,instance));
::svm_scheduler_notify(svm,s,argc,argv);
%}
INSTRUCTION scheduler.print -> STR
%{
SVM_Scheduler s = ::svm_scheduler_get(svm,CONST_PEP(scheduler,instance));
SVM_String ss = ::svm_scheduler_print(svm,s);
return NEW_VALUE(string,ss);
%}
svm_scheduler_get permet de récupérer une instance d'ordonnanceur à partir de son nom sous forme de point d'entrée d'extension.svm_scheduler_print permet d'obtenir une version textuelle de l'état d'un ordonnanceur à partir de son instance.Vous venez de voir comment créer et manipuler un ordonnanceur depuis une extension.
La création d'un nouvel ordonnanceur devrait être plutôt rare dans la vie d'un utilisateur. Cependant, bien comprendre comment s'écrit un ordonnanceur peut fortement aider à comprendre leur utilisation dans une application.
Cela permet également de personnaliser une application en y ajoutant un ordonnanceur complètement nouveau, pour atteindre des objectifs de performance tout en préservant les ressources processeur du système hôte.