blob: f6acf70e650015895e3a76be69b502c4176ea144 (
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
/*
* private.c
*
* Description:
* This translation unit implements routines which are private to
* the implementation and may be used throughout it.
*/
#include "pthread.h"
#include "implement.h"
/* Thread ID management.
---------------------
We started by simply mapping the Win32 thread handle to directly to
pthread_t. Then, is order to process pthread_join()'s, needed to be
able to keep our POSIX thread ID (pthread_t) around after the Win32
thread has terminated and possibly reused the Win32 handle.
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
occation 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[].
The code for choosing a new (pthread_t) thread from the pool of
free thread structs looks like:
if (_pthread_reuse_top == -1)
{
if (_pthread_virgin_next >= PTHREAD_THREADS_MAX)
{
return EAGAIN;
}
else
{
thread = _pthread_virgin[_pthread_virgin_next++];
}
}
else
{
thread = _pthread_reuse[_pthread_reuse_top--];
}
The code to free a thread is:
_pthread_reuse[++_pthread_reuse_top] = thread;
*/
int
_pthread_new_thread(pthread_t * thread)
{
pthread_t new_thread;
if (_pthread_reuse_top == -1)
{
if (_pthread_virgin_next >= PTHREAD_THREADS_MAX)
{
return EAGAIN;
}
else
{
new_thread = _pthread_virgin[_pthread_virgin_next++];
}
}
else
{
new_thread = _pthread_reuse[_pthread_reuse_top--];
}
new_thread->win32handle = 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->destructorstack = NULL;
new_thread->forkpreparestack = NULL;
new_thread->forkparentstack = NULL;
new_thread->forkchildstack = NULL;
*thread = new_thread;
return 0;
}
pthread_t
_pthread_find_thread((HANDLE) win32handle)
{
/* FIXME: No-op at present */
}
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(&(entry->attr));
thread->win32handle = NULL;
thread_ptstatus = _PTHREAD_REUSE;
_pthread_reuse[++_pthread_reuse_top] = thread;
}
else
{
return EINVAL
}
return 0;
}
|