[11f26f]: private.c Maximize Restore History

Download this file

private.c    178 lines (135 with data), 4.6 kB

  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
167
168
169
170
171
172
173
174
175
176
177
/*
* 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 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[].
The code for choosing a new (pthread_t) thread from the pool of
free thread structs looks like:
if (_pthread_reuse_top >= 0)
{
new_thread = _pthread_reuse[_pthread_reuse_top--];
}
else
{
if (_pthread_virgin_next < PTHREAD_THREADS_MAX)
{
new_thread = _pthread_virgin[_pthread_virgin_next++];
}
else
{
return EAGAIN;
}
}
The code to free a thread is:
_pthread_reuse[++_pthread_reuse_top] = thread;
We still need a means for pthread_self() to return its own thread
ID.
We use the Win32 Thread Local Storage mechanism. A single call to
TlsAlloc() will make available a single 32 bit location to every
thread in the process, including those created after the call is
made.
Provided we don't need to call pthread_self() after the Win32
thread has terminated we can use the DLL entry point routine to
initialise TLS for each thread. Or we can use pthread_once() in
pthread_create() to do it.
We can use either option. We'll use the DLL entry point routine.
*/
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->destructorstack = NULL;
new_thread->forkpreparestack = NULL;
new_thread->forkparentstack = NULL;
new_thread->forkchildstack = NULL;
*thread = new_thread;
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;
}
else
{
return EINVAL;
}
return 0;
}