4D v16.3

Process 4D préemptifs

Accueil

 
4D v16.3
Process 4D préemptifs

Process 4D préemptifs  


 

 

4D Developer Edition 64 bits pour OS X (ainsi que pour Windows à compter de 4D v16 R2) proposent une puissante fonctionnalité : la possibilité d'exécuter du code 4D dans un process préemptif. Grâce à cette option, vos applications 4D compilées pourront tirer intégralement parti des machines multi-coeurs, ce qui leur permettra d'accélérer leur exécution et de prendre en charge davantage d'utilisateurs simultanément.

Lorsqu'il est exécuté en mode préemptif, un process est dédié à un CPU (processeur). La gestion du process est alors déléguée au système, qui peut allouer chaque CPU séparément sur une machine multi-coeurs.

Lorsqu'ils sont exécutés en mode coopératif (seul mode disponible dans 4D jusqu'à 4D v15 R5), tous les process sont gérés par le thread (process système) de l'application parente et partagent le même CPU, même sur une machine multi-coeurs.

Par conséquent, en mode préemptif, les performance globales de l'application sont améliorées, particulièrement avec des machines multi-coeurs, car de multiples threads peuvent véritablement être exécutés simultanément. Les gains effectifs dépendent cependant de la nature des opérations exécutées.

En contrepartie, puisqu'en mode préemptif chaque thread est indépendant des autres et non géré directement par l'application, des conditions spécifiques sont à respecter dans les méthodes qui doivent être exécutées en préemptif. De plus, le mode préemptif est disponible uniquement dans certains contextes.

L'utilisation du mode préemptif est disponible uniquement dans les versions 64 bits de 4D. Les contextes d'exécution actuellement pris en charge sont les suivants :

Exécution en préemptif
4D ServerX
4D distant-
4D localX
Mode compiléX
Mode interprété-

Si le contexte d'exécution prend en charge le mode préemptif et si la méthode est "thread-safe", un process 4D lancé à l'aide de la commande New process ou CALL WORKER, ou via la commande de menu "Exécuter méthode", sera exécuté dans un thread préemptif.

Sinon, si vous appelez New process ou CALL WORKER depuis un contexte d'exécution qui n'est pas pris en charge (par exemple sur un poste 4D distant), le process sera toujours coopératif.

Note : Vous pouvez exécuter un process en mode préemptif depuis un 4D distant en démarrant une procédure stockée sur le serveur via le langage, par exemple avec Execute on server.

Le code 4D code peut être exécuté en mode préemptif uniquement lorsque certaines conditions sont réunies. Chaque partie du code exécution (commandes, méthodes, variables, etc.) doit être compatible avec une utilisation en mode préemptif. Les éléments éligibles au mode préemptif sont qualifiés de thread-safe et ceux qui ne sont pas éligibles au mode préemptif sont qualifiés de thread-unsafe.

Note : Comme un thread est traité de manière indépendante à partir de la méthode du process parent, la totalité de la chaîne d'appel ne doit contenir aucun élément thread-unsafe ; sinon, l'exécution en mode préemptif ne sera pas possible. Ce point est abordé en détail dans le paragraphe Quand un process est-il démarré en préemptif ?.

La propriété "thread safe" de chaque élément dépend de l'élément lui-même :

  • Commandes 4D : l'éligibilité au mode préemptif est une propriété interne. Dans le manuel Langage de 4D, les commandes "thread-safe" sont identifiées par l'icône . Une grande partie des commandes 4D est d'ores et déjà éligible au mode préemptif. 
  • Méthodes projet : les conditions d'éligibilité au mode préemptif sont décrites dans le paragraphe Ecrire une méthode thread-safe.

Fondamentalement, le code destiné à être exécuté dans des threads préemptifs ne peut pas appeler d'éléments ayant des interactions extérieures telles que du code de plug-in ou des variables interprocess. L'accès aux données, cependant, est possible car le serveur de données de 4D prend en charge l'exécution en mode préemptif.

Par défaut, 4D exécute toutes les méthodes projet de vos applications en mode coopératif. Si vous voulez bénéficier de la fonctionnalité du mode préemptif, la première étape consiste à déclarer explicitement toutes les méthodes que vous souhaitez démarrer en mode préemptif à chaque fois que c'est possible -- c'est-à-dire, les méthodes que vous estimez adaptées à une exécution dans un process préemptif. Le compilateur vérifiera que ces méthodes sont réellement thread-safe (voir Ecrire une méthode thread-safe pour plus d'informations). Vous pouvez également interdire le mode préemptif pour certaines méthodes, si nécessaire.

Gardez à l'esprit que déclarer une méthode "capable" d'être utilisée en préemptif la rend éligible à l'exécution dans un process préemptif mais ne garantit pas qu'elle sera effectivement exécutée en mode préemptif dans l'application. Le démarrage d'un process en mode préemptif résulte d'une évaluation effectuée par 4D en fonction de toutes les méthodes de la chaîne d'appel du process (pour plus d'informations, veuillez vous reporter au paragraphe Quand un process est-il démarré en préemptif ?).

Pour déclarer une méthode éligible à l'exécution en mode préemptif, vous devez sélectionner l'option de déclaration Mode d'exécution dans la boîte de dialogue Propriétés de la méthode :

 

Les options suivantes sont proposées :

  • Peut être exécutée dans un process préemptif : En sélectionnant cette option, vous déclarez que la méthode est capable d'être exécutée dans un process préemptif et qu'elle doit donc être exécutée en mode préemptif à chaque fois que cela est possible. La propriété "preemptive" de la méthode prend pour valeur "capable".
    Lorsque cette option est sélectionnée, le compilateur de 4D vérifiera que la méthode est effectivement capable et retournera des erreurs si ce n'est pas le cas -- par exemple, si elle appelle directement ou indirectement des commandes ou d'autres méthodes qui, elles, ne peuvent pas être exécutées en mode préemptif (toute la chaîne d'appel est analysée mais les erreurs sont signalées uniquement au premier niveau). Dans ce cas, vous pourrez modifier la méthode afin de la rendre "thread-safe" ou sélectionner une autre option.
    Si l'éligibilité de la méthode au mode préemptif est confirmée par le compilateur, elle est étiquetée "thread-safe" en interne et sera exécutée en mode préemptif à chaque fois que les conditions requises seront réunies. Cette propriété atteste de l'éligibilité de la méthode au mode préemptif mais ne garantit pas que la méthode sera effectivement exécutée en mode préemptif, puisque ce mode d'exécution requiert un contexte spécifique (voir Quand un process est-il démarré en préemptif ?).
  • Ne peut pas être exécutée dans un process préemptif : En sélectionnant cette option, vous déclarez que la méthode ne doit jamais être exécutée en mode préemptif, et doit par conséquent toujours être exécutée en mode coopératif. La propriété "preemptive" de la méthode prend pour valeur "incapable".
    Lorsque cette option est sélectionnée, le compilateur de 4D ne vérifiera pas la compatibilité de la méthode avec le mode préemptif ; elle sera automatiquement étiquetée "thread-unsafe" en interne (même dans le cas où elle est théoriquement compatible). Lorsqu'elle sera appelée en exécution, cette méthode "contaminera" toutes les autres méthodes dans le même thread, les forçant à s'exécuter en mode coopératif, même si elles sont elles-mêmes "thread-safe".
  • Indifférent (défaut) : En sélectionnant cette option, vous déclarez que vous ne souhaitez pas gérer la propriété du mode préemptif pour la méthode. La propriété "preemptive" de la méthode prend pour valeur "indifferent".
    Lorsque cette option est sélectionnée, le compilateur de 4D évaluera la compatibilité de la méthode avec le mode préemptif et lui apposera l'étiquette interne "thread-safe" ou "thread-unsafe". Aucune erreur liée à l'exécution en préemptif ne sera toutefois retournée. Si la méthode est évaluée "thread-safe", à l'exécution elle n'empêchera pas l'utilisation du mode préemptif si elle est appelée dans un contexte préemptif. A l'inverse, si la méthode est évaluée "thread-unsafe", à l'exécution elle empêchera toute utilisation du mode préemptif si elle est appelée.
    A noter qu'avec cette option, quel que soit le résultat de l'évaluation de sa compatibilité avec le mode préemptif, la méthode sera toujours exécutée en mode coopératif lorsqu'elle sera appelée directement par 4D en tant que méthode parente (par exemple via la commande New process). La propriété "thread-safe" interne n'est prise en compte que lorsque la méthode est appelée par d'autres méthodes à l'intérieur de la chaîne d'appel.
 

Note : Une méthode composant déclarée "Partagée entre composants et base hôte" doit également être déclarée "capable" pour pouvoir être exécutée dans un thread préemptif par la base hôte.

Lorsque vous exportez le code de la méthode à l'aide, par exemple, de METHOD GET CODE, la propriété "preemptive" est exportée dans le commentaire "%attributes" avec la valeur "capable" ou "incapable" (la propriété n'est pas présente si l'option est "Indifférent"). Les commandes METHOD GET ATTRIBUTES et METHOD SET ATTRIBUTES permettent également de lire ou de fixer l'attribut "preemptive" avec les valeurs "indifferent", "capable" ou "incapable".

Le tableau suivant résume les effets des options de déclaration du mode préemptif :

OptionValeur de la propriété "preemptive" (interprété)Action du compilateurEtiquette interne (compilé)Mode d'exécution si la chaîne d'appel est thread-safe
Peut être exécutée dans un process préemptifcapableVérifie la compatibilité et retourne des erreurs si incompatiblethread-safePréemptif
Ne peut pas être exécutée dans un process préemptifincapableAucune évaluationthread-unsafeCoopératif
IndifférentindifferentEvaluation mais pas d'erreursthread-safe ou thread-unsafeSi thread-safe : préemptif ; si thread-unsafe : coopératif ; si appelée directement : coopératif

Rappel : L'exécution en mode préemptif est disponible en mode compilé uniquement.

En mode compilé, lorsqu'un process est créé par la commande New process ou CALL WORKER, 4D examine la propriété "preemptive" de la méthode du process (aussi appelée méthode parente) et exécute le process en mode préemptif ou coopératif en fonction de cette propriété :

  • si la méthode du process est thread-safe (confirmée par le compilateur), le process est exécuté dans un thread préemptif.
  • si la méthode du process est thread-unsafe, le process est exécuté dans un thread coopératif.
  • si la propriété "preemptive" de la méthode du process a pour valeur "indifferent", par compatibilité le process est exécuté dans un thread coopératif (même si la méthode est en fait compatible avec le mode préemptif). A noter cependant que ce principe de compatibilité est appliqué uniquement lorsque la méthode est utilisée en tant que méthode process: une méthode déclarée "indifferent" mais étiquetée en interne "thread-safe" par le compilateur peut être appelée dans un process préemptif par une autre méthode (voir ci-dessous).

La propriété thread-safe dépend en fait de la chaîne d'appel. Si une méthode déclarée "capable" appelle une méthode thread-unsafe à n'importe lequel de ses sous-niveaux, une erreur sera retournée à la compilation : si une seule méthode de la chaîne d'appel est thread-unsafe, elle "contaminera" toutes les autres méthodes et l'exécution en préemptif sera rejetée par le compilateur. Un thread préemptif ne peut être créé que lorsque la totalité de la chaîne est thread-safe et que l'option "Peut être exécutée dans un process préemptif" a été sélectionnée pour la méthode process.
D'un autre côté, une même méthode thread-safe peut être exécutée dans un thread préemptif dans une première chaîne d'appel, et dans un thread coopératif dans une seconde chaîne d'appel. 

Par exemple, considérons les méthodes projet suivantes :

  //Méthode projet MyDialog
  //contient des appels d'interface : elle sera thread-unsafe en interne
 $win:=Open form window("tools";Palette form window)
 DIALOG("tools")

  //Méthode projet MyComp
  //contient des calculs simples : elle sera thread-safe en interne
 C_LONGINT($0;$1)
 $0:=$1*2

  //Méthode projet CallDial
 C_TEXT($vName)
 MyDialog

  //Méthode projet CallComp
 C_LONGINT($vAge)
 MyCom($vAge)

L'exécution d'une méthode en mode préemptif dépendra de sa propriété "execution" et de la chaîne d'appel. Le tableau suivant illustre les diverses situations:

Déclaration et chaîne d'appelCompilationPropriété thread safety résultanteMode d'exécutionCommentaire
OKPréemptifCallComp est la méthode parente, déclarée "capable" pour le préemptif ; comme MyComp est thread-safe en interne, CallComp est thread-safe donc le process peut être préemptif
ErreurExécution impossibleCallDial est la méthode parente, déclarée "capable" ; MyDialog est "indifferent". Cependant, puisque MyDialog est thread-unsafe en interne, elle contamine la chaîne d'appel. La compilation échoue à cause du conflit entre la déclaration de CallDial et ses capacités réelles. La solution est soit de modifier MyDialog afin qu'elle devienne thread-safe de manière à obtenir une exécution en préemptif, soit de changer la propriété de déclaration de CallDial pour lancer un process coopératif
OKCoopératifComme CallDial est déclarée "incapable" pour le préemptif, sa propriété interne est thread-unsafe ; son exécution sera toujours en coopératif, quel que soit le statut de MyDialog
OKCoopératifComme CallComp est la méthode parente (propriété "Indifferent"), le process est coopératif même si toute la chaîne d'appel était thread-safe
OKCoopératifCallDial est la méthode parente (propriété "Indifferent"), donc le process est coopératif et la compilation réussit

4D vous permet d'identifier le mode d'exécution des process en compilé :

  • La commande PROCESS PROPERTIES permet de savoir si un process est exécuté en mode préemptif ou en mode coopératif.
  • L'Explorateur d'exécution et la fenêtre d'administration de 4D Server affichent des icônes spécifiques pour les process préemptifs (ainsi que pour les process worker) :
    Type de processIcône
    Procédure stockée préemptif
    Process worker préemptif
    Process worker coopératif

Pour être thread-safe, une méthode doit respecter les conditions suivantes :

  • Sa propriété 'Mode d'exécution" doit être "Peut être exécutée dans un process préemptif" ou "Indifférent"
  • Elle ne doit pas appeler de commande 4D thread-unsafe.
  • Elle ne doit pas appeler de méthode projet thread-unsafe
  • Elle ne doit pas appeler de plug-in
  • Elle ne doit pas utiliser de blocs de code Debut SQL/Fin SQL
  • Elle ne doit pas utiliser de variable interprocess(*)
  • Elle ne doit pas appeler d'objets d'interface(**) (à quelques exceptions près, voir ci-dessous).

Note : Dans le cas d'une méthode "Partagée entre composants et base hôte", la propriété "Peut être exécutée dans un process préemptif" doit obligatoirement être sélectionnée. 

(*) Les process de type Worker vous permettent d'échanger des données entre n'importe quel process, y compris des process préemptifs. Pour plus d'informations, reportez-vous à la section A propos des workers.
(**) La commande CALL FORM fournit une solution élégante permettant d'appeler des objets d'interface depuis un process préemptif. 

Les méthodes pour lesquelles la propriété "Peut être exécutée dans un process préemptif" a été cochée seront vérifiées par 4D durant la compilation. Une erreur de compilation est générée à chaque fois que le compilateur détecte un élément non compatible avec la propriété thread-safe :

Le fichier de symboles, s'il est activé, contient également le statut thread safe pour chaque méthode :

Puisqu'il s'agit d'accès "externes", les appels aux objets d'interface utilisateur tels que les formulaires ou le Débogueur ne ne sont pas possibles dans les threads préemptifs.

Les seuls accès à l'interface utilisateur autorisés depuis un thread préemptif sont :

  • la boîte de dialogue d'erreur standard. Cette boîte de dialogue est affichée dans le process du mode utilisateur (avec 4D monoposte) ou dans le process serveur d'interface utilisateur (4D Server). Le bouton Trace est toutefois désactivé.
  • les indicateurs de progression standard
  • les boîtes de dialogue ALERTE, Demander et CONFIRMER. Cette boîte de dialogue est affichée dans le process du mode utilisateur (avec 4D monoposte) ou dans le process serveur d'interface utilisateur (4D Server).
    Notez que si 4D Server a été lancé en service sous Windows sans autorisation d'interaction utilisateur, les boîtes de dialogue ne sont pas affichées.

Un nombre important de commandes 4D sont "thread-safe". Dans la documentation, l'icône située dans la zone de propriété de commande indique que la commande est thread-safe. Vous pouvez obtenir la liste des commandes thread-safe en cliquant sur cette icône dans le manuel Langage.

Vous pouvez également utiliser la commande Command name qui peut retourner la valeur de la propriété "thread-safe" pour chaque commande.

Lorsqu'une méthode utilise une commande pouvant potentiellement déclencher un trigger, le compilateur de 4D évalue la propriété "thread-safe" du trigger afin de déterminer la compatibilité de la méthode avec le mode préemptif :

 SAVE RECORD([Table_1]//le trigger sur Table_1, s'il existe, doit être thread-safe

Voici la liste des commandes qui entraînent l'analyse du trigger à la compilation :

Si la table est passée dynamiquement, il est possible que le compilateur ne soit pas en mesure de déterminer le trigger à évaluer. C'est le cas par exemple dans les situations suivantes :

 DEFAULT TABLE([Table_1])
 SAVE RECORD
 
 SAVE RECORD($ptrOnTable->)
 SAVE RECORD(Table(myMethodThatReturnsATableNumber())->)

Dans ce cas, tous les triggers sont évalués. Si une commande thread-unsafe est détectée dans au moins un trigger, l'ensemble est rejeté et la méthode est déclarée thread-unsafe.

Les méthodes d'interception d'erreurs installées par la commande ON ERR CALL doivent être thread-safe si elles sont susceptibles d'être appelées depuis un process préemptif. Afin de gérer ce cas, le compilateur vérifie le caractère "thread safe" des méthodes d'appel sur erreur passées à la commande ON ERR CALL au moment de la compilation et retourne des erreurs si elles ne sont pas compatibles avec l'exécution en mode préemptif.

A noter que cette vérification est possible uniquement lorsque le nom de la méthode est passé en constante et n'est pas calculé, comme décrit ci-dessous :

 ON ERR CALL("myErrMethod1") //sera vérifiée par le compilateur
 ON ERR CALL("myErrMethod"+String($vNum)) //ne sera pas vérifiée par le compilateur

De plus, à compter de 4D v15 R5, si une méthode projet d'appel sur erreur ne peut pas être appelée à l'exécution (à la suite d'un problème lié au mode préemptif ou pour toute raison, comme par exemple "méthode non trouvée"), une nouvelle erreur est générée : -10532 "Impossible d'ouvrir la méthode d'appel sur erreur 'nomMéthode'".

Un process peut déréférencer un pointeur pour accéder à la valeur d'une autre variable process uniquement si les deux process sont coopératifs ; sinon, 4D retournera une erreur.

Exemples avec les méthodes suivantes :

Méthode 1 :

 myVar:=42
 $pid:=New process("Method2";0;"process name";->myVar)

Méthode 2 :

 $value:=$1->

Si le process exécutant Méthode 1 ou le process exécutant Méthode 2 est préemptif, l'expression "$value:=$1->" provoquera une erreur d'exécution.

L'utilisation des paramètres de type RefDoc (référence de document ouvert, utilisée ou retournée par les commandes Open document, Create document, Append document, CLOSE DOCUMENT, RECEIVE PACKET, SEND PACKET) est limitée aux contextes suivants :

  • Lorsqu'elle est appelée depuis un process préemptif, une référence RefDoc est utilisable uniquement depuis ce process préemptif.
  • Lorsqu'elle est appelée depuis un process coopératif, une référence RefDoc est utilisable depuis n'importe quel autre process coopératif.

Pour plus d'informations sur la référence RefDoc, veuillez vous reporter à la section RefDoc : numéro de référence de document.



Voir aussi  

Command name
Utiliser des process Web préemptifs

 
PROPRIÉTÉS 

Produit : 4D
Thème : Process

 
HISTORIQUE 

Créé : 4D v15 R5

 
UTILISATION DE L'ARTICLE

4D - Langage ( 4D v16.1)
4D - Langage ( 4D v16.2)
4D - Langage ( 4D v16.3)