Processes and Workers
Multi-tasking in 4D is the ability to have multiple operations that are executed simultaneously. These operations are called processes. Multiple processes are like multiple users on the same computer, each working on his or her own task. This essentially means that each method can be executed as a distinct database task.
If you write thread-safe code, you can create preemptive processes that will be able to take advantage of multi-core computers in your compiled applications, for faster execution.
The 4D application creates processes for its own needs, for example the Main process to manage the display windows of the user interface, the Design process to manages the windows and editors of the Design environment (note that both are worker processes), the Web Server process, the Cache Manager process, the Indexing process, or the On Event Manager process.
Creating and clearing processes
There are several ways to create a new process:
- Execute a method in the Design environment after checking the New Process check box in the "Execute Method" dialog box. The method chosen in the Execute Method dialog box is the process method.
- Use the
New processcommand. The method passed as a parameter to theNew processcommand is the process method. - Use the
Execute on servercommand in order to create a stored procedure on the server. The method passed as a parameter of the command is the process method. - Use the
CALL WORKERcommand. If the worker process does not already exist, it is created.
In Desktop applications, processes can be run by choosing menu commands. In the Menu Bar editor, select the menu command and click the Start a New Process check box. The method associated with the menu command is the process method.
A process can be cleared under the following conditions (the first two conditions are automatic):
- When the process method finishes executing
- When the user quits from the application
- If you stop the process procedurally or use the Abort button in the Debugger or in the Runtime Explorer
- If you call the
KILL WORKERcommand (to delete a worker process only).
A process can create another process. Processes are not organized hierarchically—all processes are equal, regardless of the process from which they have been created. Once the “parent” process creates a “child” process, the child process will continue regardless of whether or not the parent process is still executing.
Elements of a process
Each process contains specific elements that it can handle independently from other processes.
Language elements
- Variables: Every process has its own process variables. Process variables are recognized only within the domain of their native process.
- Process sets: Each process has its own process sets.
LockedSetis a process set. Process sets are cleared as soon as the process method ends. - Error-handling method: Each process can have its own error-handling method.
- Debugger window: Each process can have its own Debugger window.
Interface elements
Interface elements are used in Desktop applications. They consist of the following:
- Menu bar: Each process can have its own current menu bar. The menu bar of the frontmost process is the current menu bar for the application.
- One or more windows: Each process can have more than one window open simultaneously. On the other hand, some processes have no windows at all.
- One active (frontmost) window: Even though a process can have several windows open simultaneously, each process has only one active window. To have more than one active window, you must start more than one process.
- Input and Output forms: Default input and output forms can be set procedurally for each table in each process.
- Processes do not include menu bars by default which means that the standard Edit menu shortcuts (in particular, cut/copy/paste) are not available in process windows. When you call dialog boxes or 4D editors (form editor, query editor, Request, etc.) from a process, if you want for the user to be able to benefit from keyboard shortcuts like copy/paste, you need to make sure that the equivalent of an Edit menu is installed in the process.
- Preemptive processes and processes that are executed on the server (stored procedures) must not contain elements of the interface.
Each process also has a separate current selection and current record per table. For more information about these concepts, please refer to doc.4d.com.
Remote processes
When you create a process on a remote 4D, a "twin" process is created on the server to handle data access and database context as soon as it is necessary, i.e. the first time the process on the remote 4D needs to access data.
For optimization reason, while no server access is required, for example if the process on the remote 4D runs an event-handling method or controls floating windows, no twin process is created on the server.
Worker processes
Using a Worker process is a simple and powerful way to exchange information between processes. This feature is based upon an asynchronous messaging system that allows processes and forms to be called and asked to execute methods with parameters in their own context.
A worker can be "hired" by any process (using the CALL WORKER command) to execute project methods with parameters in their own context, thus allowing access to shared information.
In Desktop applications, a project method can also be executed with parameters in the context of any form using the CALL FORM command.
This functionality addresses the following needs regarding 4D interprocess communication:
- Since they are supported by both cooperative and preemptive processes, they are the perfect solution for interprocess communication in preemptive processes (interprocess variables are deprecated and not allowed in preemptive processes).
- They provide a simple alternative to semaphores, which can be cumbersome to set and complex to use
Although they have been designed mainly for interprocess communication in the context of preemptive processes, CALL WORKER and CALL FORM can be used with cooperative processes.
Using workers
A worker is used to ask a process to execute project methods. A worker consists of:
- a unique name (warning: the name is case sensitive), also used to name its associated process
- an associated process, that may or may not exist at a given moment
- a message box
- a startup method (optional)
You ask a worker to execute a project method by calling the CALL WORKER command. The worker and its message box are created at first use; its associated process is also automatically launched at first use. If the worker process dies thereafter, the message box remains open and any new message in the box will start a new worker process.
The following animation illustrates this sequence:

Unlike a process created with the New process command, a worker process remains alive after the execution of the process method ends. This means that all method executions for the same worker will be run in the same process, which maintains all process state information (process variables, current record and current selection, etc.). Consequently, methods executed successively will access and thus share the same information, allowing communication between processes. The worker's message box handles successive calls asynchronously.
CALL WORKER encapsulates both the method name and command arguments in a message that is posted in the worker's message box. The worker process is then started, if it does not already exist, and asked to execute the message. This means that CALL WORKER will usually return before the method is actually executed (processing is asynchronous). For this reason, CALL WORKER does not return any value. If you need a worker to send information back to the process which called it (callback), you need to use CALL WORKER again to pass the information needed to the caller. Of course, in this case, the caller itself must be a worker.
It is not possible to use CALL WORKER to execute a method in a process created by the New process command. Only worker processes have a message box and can thus be called by CALL WORKER. Note that a process created by New process can call a worker, but cannot be called back.
Worker processes can be created on 4D Server through stored procedures: for example, you can use the Execute on server command to execute a method that calls the CALL WORKER command.
A worker process is closed by a call to the KILL WORKER command, which empties the worker's message box and asks the associated process to stop processing messages and to terminate its current execution as soon as the current task is finished.
The startup method of a worker is the method used to create the worker (at first use). If CALL WORKER is called with an empty method parameter, then the startup method is automatically reused as method to execute.
The main process created by 4D when opening a database for user and application modes is a worker process and can be called using CALL WORKER. Note that the name of the main process may vary depending on the 4D localization language, but it always has the process number 1; as a result, it's more convenient to designate it by process number instead of process name when calling CALL WORKER.
Identifying Worker processes
All worker processes, except the main process, have the process type Worker process (5) returned by the Process info command.
Specific icons identify worker processes.
See also
For more information, please see this blog post about how to use workers.
Semaphores
Semaphores allow you to make sure that two or more processes do not modify the same resource (file, record...) at the same time. Only the process that sets the semaphore can remove it.
Signals can also be used to manage interactions. Signals allow you to make sure one or more process(es) will wait for a specific task to be completed before continuing execution. Any process can wait and/or release a signal.
What is a semaphore?
In a computer program, a semaphore is a tool used to protect actions that need to be performed by only one process or user at a time.
In 4D, the conventional need for using semaphores is for modifying an interprocess array: if one process is modifying the values of the array, another process must not be able to do the same thing at the same time. The developer uses a semaphore to indicate to a process that it can only perform its sequence of operations if no other process is already performing the same tasks. When a process encounters a semaphore, there are three possibilities:
- It immediately gets the right to pass
- It waits for its turn until it gets the right to pass
- It continues on its way, abandoning the idea of performing the tasks.
The semaphore therefore protects parts of the code. It allows only one process through at a time and blocks access until the process currently holding the right of use relinquishes this right by releasing the semaphore.
Commands for working with semaphores
In 4D, you set a semaphore by calling the Semaphore command. To release a semaphore, you call the CLEAR SEMAPHORE command.
The Semaphore command has a very special behavior since it potentially performs two actions simultaneously:
- If the semaphore is already assigned, the function returns True
- If the semaphore is not assigned, the function assigns it to the process and returns False at the same time.
This double action performed by the same command ensures that no external operation can be inserted between the semaphore test and its assignment.
You can use the Test semaphore command to find out whether a semaphore is already assigned or not. This command is mainly used as part of long operations, such as the annual closing of accounts, where Test semaphore lets you control the interface to prevent access to certain operations such as the addition of accounting data.
How to use semaphores
Semaphores should be used according to the following principles:
- A semaphore must be set and released in the same method,
- The execution of code protected by the semaphore must be as short as possible,
- The code must be timed by means of the tickCount parameter of the
Semaphorecommand to wait for the release of the semaphore.
Here is typical code for using a semaphore:
While(Semaphore("MySemaphore";300))
IDLE
End while
// place code protected by semaphore here
CLEAR SEMAPHORE("MySemaphore")
A semaphore that is not released can block part of the database. Setting and releasing semaphores in the same method helps to eliminate this risk.
Minimizing the code protected by the semaphore increases the fluidity of the application and avoids the semaphore acting as a bottleneck.
Finally, using the optional tickCount parameter of the Semaphore command is essential for optimizing the wait for the semaphore to be released. Using this parameter, the commands works as follows:
- The process waits a maximum of the specified number of ticks (300 in the example) for the semaphore to be available, without the code execution moving on to the next line,
- If the semaphore is released before the end of this limit, it is immediately assigned to the process (Semaphore returns False) and code execution resumes,
- If the semaphore is not released before the end of this limit, then code execution resumes.
The command also prioritizes requests by establishing a queue. This way, the first process requesting a semaphore will be the first to get one. Note that the wait time is set depending on the specifics of the application.
Local or global semaphores
There are two types of semaphores in 4D: local semaphores and global semaphores.
- A local semaphore is accessible by all processes on the same workstation and only on the workstation. A local semaphore can be created by prefixing the name of the semaphore with a dollar sign ($). You use local semaphores to monitor operations among processes executing on the same workstation. For example, a local semaphore can be used to monitor access to an interprocess array shared by all the processes in your single-user database or on the workstation.
- A global semaphore is accessible to all users and all their processes. You use global semaphores to monitor operations among users of a multi-user database.
Global and local semaphores are identical in their logic. The difference resides in their scope.
In client/server, global semaphores are shared among all the processes running on all clients and servers. A local semaphore is only shared among the processes running on the machine where it has been created.
In single-user 4D applications, global or local semaphores have the same scope because you are the only user. However, if your database is being used in both setups, make sure to use global or local semaphores depending on what you want to do.
Note: We recommend using local semaphores when you need a semaphore to manage a local aspect for a client of the application, such as the interface or an array of interprocess variables. If you use a global semaphores in this case, it would not only cause unnecessary network exchanges but could also affect other client machines unnecessarily. Using a local semaphore would avoid these undesirable side effects.