122 | | The destruction of an user thread T can be caused by another thread C, executing the ''pthread_cancel()'' sys call requesting the target thread to stop execution. It can also be caused by the thread T itself, executing the ''pthread_exit()'' sys call to suicide. Finally, it can also be caused by the ''exit()'' or ''kill()'' syscalsl requesting the destruction of all threads of a given process. |
123 | | |
124 | | TODO : handle the special case of the main thread... |
| 122 | The destruction of an user thread T can be caused by another thread C, executing the ''pthread_cancel()'' sys call requesting the target thread to stop execution. It can be caused by the thread T itself, executing the ''pthread_exit()'' sys call to suicide. Finally, it can be caused by the ''exit()'' or ''kill()'' syscalsl requesting the destruction of all threads of a given process. |
| 123 | |
| 124 | The unique method to destroy a thread is to set the THREAD_FLAG_REQ_KILL or THREAD_FLAG_REQ_EXIT flags in the ''flags'' field of the thread descriptor. The thread will be asynchronously deleted by the scheduler at the next scheduling point. |
| 125 | It detach the thread from the scheduler, detach the thread from the local process descriptor, and releases the memory allocated to the thread descriptor. The destruction request can be done by the ''target'' thread itself (for an exit), or it can be done by another ''killer'' thread (for a kill). |
| 126 | |
| 127 | The main thread (i.e. the thread 0 in the process owner cluster) is a special case, because the main thread of a given process can only be deleted (i.e. marked for delete) by the parent process main thread executing the sys_wait() scale (see section [6] below). |
131 | | * for an exit, the calling thread sets the THREAD_FLAG_REQ_EXIT bit in the target thread ''flags'' field, |
132 | | * for a cancel, the calling thread sets the THREAD_FLAG_REQ_KILL bit in the target thread ''flags'' field, |
133 | | * at the next scheduling point, the target scheduler, detecting the SIG_SUICIDE bit, detach the thread from the scheduler, detach the thread from the local process descriptor, and releases the memory allocated to the thread descriptor. |
134 | | At the next scheduling point, |
135 | | |
136 | | |
137 | | |
138 | | |
| 134 | * for an exit, the killer thread sets the THREAD_FLAG_REQ_EXIT bit in the target thread ''flags'' field, |
| 135 | * for a kill, the killer thread sets the THREAD_FLAG_REQ_KILL bit in the target thread ''flags'' field, |
| 136 | * the killer thread returns without waiting the actual deletion. |
| 137 | |
| 138 | |
| 139 | |
| 140 | ####### |
145 | | |
146 | | * The sys_thread_exit() function sets the SIG_SUICIDE bit in the thread "signals" bit_vector, sets the BLOCKED_GLOBAL bit in the thread "blocked" bit_vector, and de-schedule. |
147 | | |
148 | | |
149 | | === 5.3) thread running in ATTACHED mode === |
150 | | |
151 | | The thread termination is more complex if the thread T is running in ATTACHED mode, because another - possibly remote - PT thread, executing the ''pthread_join'' system call, must be informed of the termination of thread T. As the '''sys_thread_exit()''' (or ''sys_thread_cancel()'') function on one hand, and the '''sys_thread_join()''' on the other hand, can be executed in any order, this requires a "rendez-vous": The first arrived thread block and deschedule, and must be reactivated by the other thread. This synchronisation uses three specific fields in the thread descriptor: the "join_lock" field is a remote_spin_lock; the "join_value" field contains the exit value returned by the finishing thread T; the "join_xp"field contains an extended pointer on the PT thread that wants to join. It uses one specific JOIN_DONE flag in the thread descriptor "flags" field. The scenario is not symmetrical, because the PT thread can access the T thread descriptor at any time, but the T thread cannot access the PT thread descriptor before the pthread_join execution: |
152 | | |
153 | | * Both the T thread (executing the sys_thread_exit() function), and the PT thread (executing the sys_thread_join() function) try to take the "join_lock" implemented in the T thread descriptor (the "join_lock" in the PT thread is not used). |
154 | | * The T thread registers its exit value in the T thread "join_value" field, and test the JOIN_DONE flag in the T thread "flags" field: |
155 | | * If the JOIN_DONE flag is set, the PT thread arrived first and is blocked: the T thread reset the BLOCKED_EXIT bit in the PT thread (using the extended pointer stored in the "join_xp" field), reset the JOIN_DONE flag, releases the "join_lock" in T thread, and exit as described in the DETACHED case. |
156 | | * If the JOIN_DONE flag is not set, the T thread T arrived first: the T thread set the BLOCKED_JOIN bit in the T thread "blocked" field, releases the "join"lock", and deschedules. |
157 | | * The PT thread test the BLOCKED_JOIN bit in T thread: |
158 | | * If the BLOCKED_JOIN bit is set, the T thread arrived first and is blocked: the PT thread reset the BLOCKED_JOIN bit in the T thread, get the exit value from the T thread i "join_value" field, releases the "join_lock" in T thread, and continue. |
159 | | * If the BLOCKED_JOIN bit is not set, the PT thread arrived first: the PT thread register its extended pointer in the T thread "join_xp" field, set the JOIN_DONE flag in the T thread, sets the BLOCKED_EXIT bit in the PT thread "blocked" field, releases the "join_lock" in the T thread, and deschedules. |
| 148 | The thread destruction is more complex if the target thread T is running in ATTACHED mode, because another - possibly remote - J thread, executing the ''pthread_join'' system call, must be informed of the termination of thread T. As the ''thread_kill()'' function, executed by the killer thread K, and the ''sys_thread_join()'', executed by the joining thread J, can be executed in any order, this requires a "rendez-vous": The first arrived thread blocks and deschedules. It will be unblocked by the other thread. |
| 149 | |
| 150 | Therefore, the destruction mechanism can involve three threads: the target thread T, the killer thread K, and the joining thread J: |
| 151 | |
| 152 | It uses three specific fields in the thread descriptor: the ''join_lock'' field is a remote_spin_lock; the ''join_value'' field contains the exit value returned by the T thread (only used for an exit); the ''join_xp'' field contains an extended pointer on the first arrived thread. It uses also two specific THREAD_FLAG_JOIN_DONE and THREAD_FLAG_KILL_DONE flags in the thread descriptor ''flags'' field, and one specific blocking bit THREAD_BLOCKED_JOIN, in the ''blocked'' field. |
| 153 | |
| 154 | * Both the killer thread K, executing the thread_kill() function), and the joining thread J, executing the sys_thread_join() function, try to take the ''join_lock'' implemented in the T thread descriptor (the ''join_lock'' in the J thread is not used). |
| 155 | * The K thread test the FLAG_JOIN_DONE in the T thread descriptor: |
| 156 | * If the FLAG_JOIN_DONE is set, the J thread arrived first and is blocked: the K thread reset the BLOCKED_JOIN bit in the J thread (using the extended pointer stored in the ''join_xp'' field), reset the JOIN_DONE flag, releases the ''join_lock'' in T thread, and completes the T thread destruction as described in the detached case and return. |
| 157 | * If the FLAG_JOIN_DONE is not set, the K thread arrived first: the K thread set the BLOCKED_JOIN bit in the K thread, set the FLAG_KILL_DONE in the T thread, register its extended pointer in the T thread ''join_xp'' field, releases the ''join_lock'' in the T thread, and deschedules. It will complete the T thread destruction as described in the detached case and return when it resume. |
| 158 | * The J thread test the FLAG_KILL_DONE in the T thread descriptor:s |
| 159 | * If the FLAG_KILL_DONE is set, the K thread arrived first and is blocked: the J thread reset the BLOCKED_JOIN bit in the K thread, get the exit value from the T thread "join_value" field, releases the "join_lock" in T thread, and return. |
| 160 | * If the FLAG_KILL_DONE is not set, the J thread arrived first: the J thread register its extended pointer in the T thread "join_xp" field, set the FLAG_JOIN_DONE in the T thread, sets the BLOCKED_EXIT bit in the J thread, releases the "join_lock" in the T thread, and deschedules. It will get the exit value from the T thread "join_value" field and return when it resumes. |