blob: 783e0ef0888c94dc923f640035a2cf80f05b5815 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
/*
* private.c
*
* Description:
* This translation unit implements routines which are private to
* the implementation and may be used throughout it.
*/
#include <errno.h>
#include "pthread.h"
#include "implement.h"
/* Thread ID management.
---------------------
We started by simply mapping the Win32 thread handle directly to
pthread_t. However, in order to process pthread_join()'s, we need
to be able to keep our POSIX thread ID (pthread_t) around after the
Win32 thread has terminated. Win32 may reuse the Win32 handle during that
time, which will conflict.
The pthread_t value is now actually the pointer to a thread struct:
typedef struct _pthread * pthread_t;
which amongst other things stores the Win32 thread handle:
struct _pthread {
HANDLE win32handle;
int ptstatus;
...
};
So now whereever we need to use the Win32 handle it can be accessed
as:
pthread_t T = pthread_this();
HANDLE H;
H = T->win32handle;
// or (which is NOT preferred, let the compiler optimise to this).
H = (HANDLE) *T;
POSIX Threads Table
-------------------
Having the thread ID as a pointer to the thread struct itself
avoids the need to search the threads table in all but the initial
occasion where we create the thread.
Initially we used a hash function to select a free thread struct
from the table, possibly needing a walk through the table if the
hash collided with an already in-use thread.
The scheme used now is more efficient and is done as follows:
We use two tables and two counters:
struct _pthread _pthread_virgins[PTHREAD_THREADS_MAX];
pthread_t _pthread_reuse[PTHREAD_THREADS_MAX];
int _pthread_virgin_next = 0;
int _pthread_reuse_top = -1;
The counter _pthread_virgin_next is an index into _pthread_virgins[],
which can be thought of as a list, and _pthread_reuse_top is an
index into _pthread_reuse[], which can be thought of as a LIFO stack.
Once taken from _pthread_virgins[], used and freed threads are only
ever pushed back onto _pthread_reuse[].
*/
int
_pthread_new_thread(pthread_t * thread)
{
pthread_t new_thread;
if (_pthread_reuse_top >= 0)
{
new_thread = _pthread_reuse[_pthread_reuse_top--];
}
else
{
if (_pthread_virgin_next < PTHREAD_THREADS_MAX)
{
new_thread = (pthread_t) &_pthread_virgins[_pthread_virgin_next++];
}
else
{
return EAGAIN;
}
}
new_thread->win32handle = (HANDLE) NULL;
new_thread->ptstatus = _PTHREAD_NEW;
pthread_attr_init(&(new_thread->attr));
new_thread->joinvalueptr = NULL;
new_thread->cancelstate = PTHREAD_CANCEL_ENABLE;
new_thread->canceltype = PTHREAD_CANCEL_DEFERRED;
new_thread->cancel_pending = FALSE;
new_thread->cleanupstack = NULL;
new_thread->forkpreparestack = NULL;
new_thread->forkparentstack = NULL;
new_thread->forkchildstack = NULL;
*thread = new_thread;
_pthread_threads_count++;
return 0;
}
int
_pthread_delete_thread(_pthread_t * thread)
{
/* We don't check that the thread has been properly cleaned up, so
it had better be done already. */
/* Remove the thread entry if necessary. */
if (thread != NULL
&& thread->ptstatus == _PTHREAD_EXITED)
{
pthread_attr_destroy(&(thread->attr));
thread->win32handle = (HANDLE) NULL;
thread->ptstatus = _PTHREAD_REUSE;
_pthread_reuse[++_pthread_reuse_top] = thread;
_pthread_threads_count--;
return 0;
}
return EINVAL;
}
|