mirror of
https://github.com/EiffelSoftware/eiffel-org.git
synced 2025-12-08 15:52:26 +01:00
Added doc for 21.11
git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@2328 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
[[Property:title|Compilation of multithreaded systems]]
|
||||
[[Property:weight|3]]
|
||||
[[Property:uuid|24e474b5-3cb7-16ff-365f-73be8e73bd56]]
|
||||
{{UpdateNeeded}}
|
||||
|
||||
|
||||
{{ReviewRequested}}
|
||||
|
||||
|
||||
==Settings==
|
||||
* Set the Concurrency option to EiffelThread in the project settings under Advanced.
|
||||
* Do not use a non-multi-threaded precompiled library. The corresponding multi-threaded libraries of base, WEL and Vision2 should be located in $ISE_EIFFEL/precomp/spec/$PLATFORM/ with the mt prefix.
|
||||
* You may want to include the thread library (located at "$ISE_EIFFEL/library/thread") in your project. This setting can be done in [[Group Options| the groups section of the project settings window]] .
|
||||
* When using external C libraries, be sure that they are MT-safe on the platforms where the Eiffel Threads are available. For example [[WEL]] and [[EiffelNet]] multi-threaded libraries can be recognized by the `mt` prefix.
|
||||
|
||||
==Compilation==
|
||||
|
||||
Just launch the compilation: the Eiffel compiler will generate and link the multi-threaded executable. If the project was already compiled, [[Clean compile|clean the project and recompile it from scratch]].
|
||||
|
||||
==External C files==
|
||||
|
||||
The C files that you link with a multi-threaded Eiffel application must be compiled in multi-threaded mode. In addition, you should follow some basic guidelines, which you can find in any documentation on threads. Here are the main requirements:
|
||||
* Check that what you are importing is safe: you cannot arbitrarily enter non-threaded code in a multi-threaded program. Check your include files and the libraries that you use for linking to decide if they can be used in multi-threaded mode.
|
||||
* If you are using CECIL in multi-threaded mode, you must compile your C files with the same defined symbols as those used to compile the generated C-code in multi-threaded mode.
|
||||
* Threaded code can safely refer to unsafe code ONLY from the initial thread.
|
||||
|
||||
{{note|If you use the libraries EiffelNet and MEL in multi-threaded mode, you should use libmtnet. a and libmtmel. a. When using MEL, you have to be aware that Motif 1. 2 is not threaded-safe (i.e not reentrant). Motif 2. x is threaded-safe.}}
|
||||
|
||||
==Thread Safe Eiffel libraries==
|
||||
|
||||
Most libraries are not thread safe. This means that even though you can use them in a multi-threaded environment, you will need to add the necessary protection to ensure that objects are accessed only by one thread at a time.
|
||||
|
||||
Most of the issues are related to the use of EiffelBase containers which were designed at a time when threads were not present. In the future we will certainly provide a version of EiffelBase which will be designed to be thread safe, in the meantime, you have to use basic protection mechanisms such as mutexes to guarantee the safe execution of your programs.
|
||||
|
||||
Here is a summary of what you need to include in your project settings when using our libraries:
|
||||
|
||||
- EiffelBase: not thread safe, no externals required
|
||||
|
||||
- WEL: partly thread safe regarding creation of graphical objects that belong to two different threads, but since it relies on EiffelBase, be aware of what has been said. Other libraries have the same dependency on EiffelBase and this warning will not be repeated.
|
||||
|
||||
- MEL: you have to be aware that Motif 1. 2 is not thread safe and that Motif 2.x is. Therefore you can use MEL in a multi-threaded program only if you have Motif 2.x. In this case, you need to include libmtmel.a instead of libmel.a
|
||||
|
||||
- EiffelNet: thread safe with above limitation on EiffelBase.
|
||||
|
||||
- COM: not thread safe.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[[Property:title|EiffelThread Tutorial]]
|
||||
[[Property:weight|-1]]
|
||||
[[Property:uuid|c1965bc3-23cf-25d6-02c1-717b7035d41c]]
|
||||
Multithreaded applications provide a flexible, exciting way of utilizing the power of modern computer systems. EiffelThread supports a powerful multithreaded model, fast and easy to use.
|
||||
|
||||
The EiffelThread library is mapped to the native thread library of your platform such as Windows, POSIX, Solaris and Unix International Threads.
|
||||
|
||||
This document will explain how to use the EiffelThread library. To fully take advantage of this documentation you should know some basics about multithreaded systems
|
||||
|
||||
To enable multithreading in the EiffelStudio environment, you will need to do some changes in your project settings.
|
||||
|
||||
There are special considerations for using "once" features in an Eiffel multithreaded environment.
|
||||
|
||||
If you encounter problems, take a look at the FAQ list.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
[[Property:title|Multithread FAQ]]
|
||||
[[Property:weight|6]]
|
||||
[[Property:uuid|a0e1ddf6-cc19-b6f8-0e05-075908ddd347]]
|
||||
===I've launched several threads and they do not seem to be executed:===
|
||||
|
||||
The thread that launched the several threads may be dead before its children.
|
||||
|
||||
===Two threads can lock the same mutex, however none of them unlocks it:===
|
||||
|
||||
Same problem as above. Maybe the first thread that locked the shared mutex died before the second tried to lock it: thus, the first one automatically unlocked it when it died. You should put a join_all or an infinite loop in the parent thread.
|
||||
|
||||
===I've added the option multithreaded in the project settings and it crashes:===
|
||||
|
||||
If you have already compiled your system in non-MT mode, you cannot change the mode of compilation and simply relaunch your compilation (the generated C-code would be incompatible). Delete all your object files in your W_code or F_code directory and freeze or finalize the system again.
|
||||
|
||||
===My once function changed during my MT-Eiffel-program:===
|
||||
|
||||
The once functions are once per thread in Multithreaded mode. Hence, each once function is thread-specific and is initialized the first time it is called in a thread.
|
||||
|
||||
You can create a once per process by following [[Once features in multithreaded mode|these instructions]] .
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
[[Property:title|Once features in multithreaded mode]]
|
||||
[[Property:weight|4]]
|
||||
[[Property:uuid|5578da29-7603-b501-1a7d-305d20fd6485]]
|
||||
==Manipulating Once features in multithreaded mode==
|
||||
|
||||
Eiffel introduced the powerful mechanism of once routines. A once routine has a body that will be executed only once, at the first call. Subsequent calls will have no further effect and, in the case of a function, will return the same result as the first. This provides a simple way of sharing objects in an object-oriented context.
|
||||
|
||||
For multithreaded applications, the appropriate semantics is that once routines must be called once per thread (rather than once per process). This is the semantics supported by EiffelThread.
|
||||
|
||||
Then the once feature is not initialized once per process but once per thread. Your once feature will be called again in any new thread execution.
|
||||
|
||||
==Once per Process/Thread==
|
||||
|
||||
By default, once features are once per thread. This means that when a once feature is called in a thread, the Eiffel run-time will check whether it has been already computed in this thread. If not, the once feature will be initialized and computed. This corresponds to the most commonly desired behavior for once features in multithreaded mode: most of the time, a once called in a thread is not likely to share its result with other threads.
|
||||
|
||||
This is only the default, however: you may at times need to use "once per process" versus "once per thread". Objects created "once per process" in multithreading mode can be shared among threads. You can use a [[ET: Once routines and shared objects#Adjusting once semantics with "once keys"|'''once key''']] to indicate the mode you wish to use.
|
||||
|
||||
==Specifying once per thread or once per process ==
|
||||
|
||||
As mentioned above, in multithreaded mode, the default <code>once</code> syntax will ensure "once per thread", as in this example:
|
||||
|
||||
<code>
|
||||
object_per_thread: OBJECT
|
||||
-- Once per thread.
|
||||
once
|
||||
create Result.make
|
||||
end
|
||||
</code>
|
||||
|
||||
You could obtain the same effect by explicitly coding the "THREAD" once key:
|
||||
|
||||
<code>
|
||||
object_per_thread: OBJECT
|
||||
-- Once per thread.
|
||||
once ("THREAD")
|
||||
create Result.make
|
||||
end
|
||||
</code>
|
||||
|
||||
To ensure that a once function is executed only once per process, you would use the "PROCESS" once key:
|
||||
|
||||
<code>
|
||||
object_per_process: OBJECT
|
||||
-- New 'object' (once per process)
|
||||
-- that could be shared between threads
|
||||
-- without reinitializing it.
|
||||
once ("PROCESS")
|
||||
create Result.make
|
||||
end
|
||||
|
||||
</code>
|
||||
|
||||
The same concepts apply to once procedures.
|
||||
|
||||
|
||||
{{note|Remember the effect of exceptions on the execution of once routines. If an exception occurs during the execution of a once routine, then '''that same exception will be raised again on each subsequent call''' to the once routine.}}
|
||||
|
||||
===Obsolete syntax===
|
||||
|
||||
The syntax shown above is the current standard syntax. However in Eiffel code written for previous versions, you may run across once keys for multithreaded systems which are expressed in a different syntax. Specifically, the obsolete syntax used a feature's <code>note</code> clause to specify a once key, as in the following example.
|
||||
|
||||
<code>
|
||||
object_per_process: OBJECT
|
||||
-- Obsolete syntax
|
||||
-- New 'object' (once per process)
|
||||
-- that could be shared between threads
|
||||
-- without reinitializing it.
|
||||
note
|
||||
once_status: global
|
||||
once
|
||||
create Result.make
|
||||
end
|
||||
</code>
|
||||
|
||||
|
||||
==Using once per object in a multithreaded system==
|
||||
|
||||
It is possible to use a once key to specify a once function that is executed [[ET: Once routines and shared objects#Adjusting once semantics with "once keys"|once per object]]. However, in a multithreaded system, it is important to understand that no automatic synchronization of access occurs in this case. So the following caution is given.
|
||||
|
||||
|
||||
{{caution|You should understand that in a multithreaded system, you must synchronize access to the result of a function executed once per object in the same way that you would synchronize access to class attributes. }}
|
||||
|
||||
|
||||
|
||||
{{SeeAlso|The Eiffel Tutorial section on [[ET: Once routines and shared objects]] .}}
|
||||
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
[[Property:title|Thread library overview]]
|
||||
[[Property:weight|1]]
|
||||
[[Property:uuid|2bdeeb91-1917-f443-ebfc-4f877107edd7]]
|
||||
This is only a quick overview of the EiffelThread Library. The class reference for this library should give its complete interface.
|
||||
|
||||
==Creating and launching threads: the class THREAD (deferred)==
|
||||
|
||||
The class of the thread object you want to create should inherit the <eiffel>THREAD</eiffel> class. <br/>
|
||||
Your thread is represented by a class which inherits from <eiffel>THREAD</eiffel> (deferred class). <br/>
|
||||
|
||||
<code>
|
||||
class
|
||||
MY_THREAD
|
||||
|
||||
inherit
|
||||
THREAD
|
||||
...
|
||||
|
||||
feature
|
||||
|
||||
execute
|
||||
-- define the deferred feature from THREAD.
|
||||
do
|
||||
...
|
||||
end
|
||||
|
||||
...
|
||||
|
||||
end -- class MY_THREAD
|
||||
</code>
|
||||
|
||||
|
||||
Creating a thread is like creating an Eiffel object:
|
||||
|
||||
|
||||
<code>
|
||||
my_thread: MY_THREAD
|
||||
-- MY_THREAD inherits from THREAD and defines
|
||||
-- the deferred procedure `execute'
|
||||
|
||||
...
|
||||
|
||||
create my_thread
|
||||
</code>
|
||||
|
||||
|
||||
{{note|You have created a thread object but have not started the thread itself yet. <br/>
|
||||
To run the thread, use the feature <eiffel>launch</eiffel> from <eiffel>THREAD</eiffel>. }}
|
||||
|
||||
|
||||
<code>
|
||||
my_thread.launch</code>
|
||||
|
||||
|
||||
On the Eiffel side, the procedure <eiffel>execute</eiffel> will be launched. This procedures deferred in class <eiffel>THREAD</eiffel>, you have to define it in <eiffel>MY_THREAD</eiffel>.
|
||||
|
||||
|
||||
On the C side, a C thread will be created and launched.
|
||||
|
||||
|
||||
{{caution|You may call <eiffel>join_all</eiffel> and the end of the execution of the parent thread if you do not want it to die before its child, otherwise they may prematurely terminate. }}
|
||||
|
||||
|
||||
==The class MUTEX==
|
||||
|
||||
The implementation of the class <eiffel>MUTEX</eiffel> is mapped on the C standard thread library. An instance of class <eiffel>MUTEX</eiffel> can be shared between different thread.
|
||||
|
||||
|
||||
<eiffel>my_mutex.pointer</eiffel> is the pointer to the nested C mutex of <eiffel>my_mutex</eiffel>.
|
||||
|
||||
|
||||
* Declaration of the mutex:
|
||||
<code>
|
||||
my_mutex: MUTEX</code>
|
||||
|
||||
* Creation of mutex:
|
||||
<code>
|
||||
create my_mutex.make</code>
|
||||
|
||||
* Locking the mutex:
|
||||
<code>
|
||||
my_mutex.lock</code>
|
||||
|
||||
* Unlocking the mutex:
|
||||
<code>
|
||||
my_mutex.unlock</code>
|
||||
|
||||
* <eiffel>try_lock</eiffel>: if it is not locked yet, lock the mutex and return True, otherwise it returns False.
|
||||
<code>
|
||||
my_mutex.try_lock</code>
|
||||
|
||||
* Is my mutex initialized?
|
||||
<code>
|
||||
my_mutex.is_set</code>
|
||||
|
||||
|
||||
{{note|on Windows: The <eiffel>MUTEX</eiffel> objects on Windows are recursive while they are not on Unix. A recursive mutex can be locked twice by the same thread. }}
|
||||
|
||||
|
||||
{{caution|Be sure that a mutex is unlocked when it is disposed. }}
|
||||
|
||||
|
||||
==The class SEMAPHORE==
|
||||
|
||||
Like <eiffel>MUTEX</eiffel>, the features of this class are mapped on the C thread library. An instance of class <eiffel>SEMAPHORE</eiffel> can be shared between thread.
|
||||
|
||||
|
||||
* Declaration of the semaphore :
|
||||
<code>
|
||||
my_sem: SEMAPHORE</code>
|
||||
|
||||
Creation of semaphore: initialize semaphore with nb_tokens, it requires nb_tokens > = 0
|
||||
<code>
|
||||
create my_sem.make (nb_tokens)</code>
|
||||
|
||||
* Wait for a token:
|
||||
<code>
|
||||
my_sem.wait</code>
|
||||
|
||||
* Give back a token:
|
||||
<code>
|
||||
my_sem.post</code>
|
||||
|
||||
* <eiffel>try_wait</eiffel>, similar to try_lock from <eiffel>MUTEX</eiffel>, if a token is available, take it and return <code> True </code>, otherwise return <code> False </code>.
|
||||
<code>
|
||||
my_sem.try_wait</code>
|
||||
|
||||
|
||||
{{caution|Be sure that a semaphore does not wait for a token when it is disposed }}
|
||||
|
||||
==The class CONDITION_VARIABLE==
|
||||
|
||||
This class allows to use condition variables in Eiffel. An instance of class <eiffel>CONDITION_VARIABLE</eiffel> can be shared between threads.
|
||||
|
||||
|
||||
* Declaration of the condition variable
|
||||
<code>
|
||||
my_cond: CONDITION_VARIABLE</code>
|
||||
|
||||
* Creation:
|
||||
<code>
|
||||
create my_cond.make</code>
|
||||
|
||||
* Wait for a signal (send by <eiffel>signal</eiffel>). You need to use a mutex.
|
||||
<code>
|
||||
my_mutex: MUTEX
|
||||
|
||||
...
|
||||
|
||||
create my_mutex.make
|
||||
</code>
|
||||
|
||||
|
||||
<code>my_mutex</code> must be locked by the calling thread so as <eiffel>wait</eiffel> can be called. <eiffel>wait</eiffel> atomically unlocks <code> my_mutex </code> and waits for the condition variable <code> my_mutex </code> to receive a signal. As soon as it received a signal, ''<code>my_cond </code>'' locks ''<code>my_mutex </code>''
|
||||
|
||||
<code>
|
||||
my_mutex.lock
|
||||
-- You must lock `my_mutex' before calling wait.
|
||||
|
||||
my_cond.wait (my_mutex)
|
||||
-- Here the critical code to execute when `my_cond' received a signal.
|
||||
|
||||
my_mutex.unlock
|
||||
-- Unlock the mutex at the end of the critical section.
|
||||
</code>
|
||||
|
||||
|
||||
* Send a signal one thread blocked on the condition variable `my_cond'.
|
||||
<code>
|
||||
my_cond.signal</code>
|
||||
|
||||
* Send a signal to all the threads blocked on the condition variable `my_cond'.
|
||||
<code>
|
||||
my_cond.broadcast</code>
|
||||
|
||||
|
||||
{{caution|Be sure that a condition variable is unblocked when it is disposed. }}
|
||||
|
||||
|
||||
==Miscellaneous classes==
|
||||
|
||||
class <eiffel>THREAD_ATTRIBUTES</eiffel>: defines the attributes of an Eiffel Thread regarding the thread scheduling policy and priority.
|
||||
|
||||
|
||||
==Controlling execution: THREAD_CONTROL==
|
||||
|
||||
* <eiffel>yield</eiffel>: the calling thread yields its execution in favor of an other thread of same priority.
|
||||
* <eiffel>join_all</eiffel>: the calling thread waits for all other threads to finished (all its children).
|
||||
* A parent thread can wait for the termination of a child process through the feature <eiffel>join</eiffel> of class <eiffel>THREAD_CONTROL</eiffel> (inherited by <eiffel>THREAD</eiffel>):
|
||||
<code>
|
||||
thr: MY_THREAD
|
||||
...
|
||||
|
||||
thr.launch
|
||||
...
|
||||
thr.join
|
||||
</code>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user