aboutsummaryrefslogtreecommitdiff
path: root/winsup/mingw/mthr.c
blob: f28236432a0aca69fbd7919f90ce9394251b266f (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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
 * mthr.c
 *
 * Implement Mingw thread-support DLL .
 *
 * This file is used iff the following conditions are met:
 *  - gcc uses -mthreads option 
 *  - user code uses C++ exceptions
 *
 * The sole job of the Mingw thread support DLL (MingwThr) is to catch 
 * all the dying threads and clean up the data allocated in the TLSs 
 * for exception contexts during C++ EH. Posix threads have key dtors, 
 * but win32 TLS keys do not, hence the magic. Without this, there's at 
 * least `6 * sizeof (void*)' bytes leaks for each catch/throw in each
 * thread. The only public interface is __mingwthr_key_dtor(). 
 *
 * Created by Mumit Khan  <khan@nanotech.wisc.edu>
 *
 */

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include <stdlib.h>

/* To protect the thread/key association data structure modifications. */
CRITICAL_SECTION __mingwthr_cs;

typedef struct __mingwthr_key __mingwthr_key_t;

/* The list of threads active with key/dtor pairs. */
struct __mingwthr_key {
  DWORD key;
  void (*dtor) (void *);
  __mingwthr_key_t *next;
};


static __mingwthr_key_t *key_dtor_list;

/*
 * __mingwthr_key_add:
 *
 * Add key/dtor association for this thread. If the thread entry does not
 * exist, create a new one and add to the head of the threads list; add
 * the new assoc at the head of the keys list. 
 *
 */

static int
___mingwthr_add_key_dtor ( DWORD key, void (*dtor) (void *))
{
  __mingwthr_key_t *new_key;

  new_key = (__mingwthr_key_t *) calloc (1, sizeof (__mingwthr_key_t));
  if (new_key == NULL)
    return -1;
  
  new_key->key = key;
  new_key->dtor = dtor;

  EnterCriticalSection (&__mingwthr_cs);

  new_key->next = key_dtor_list;
  key_dtor_list = new_key;

  LeaveCriticalSection (&__mingwthr_cs);

#ifdef DEBUG
  printf ("%s: allocating: (%ld, %x)\n", 
          __FUNCTION__, key, dtor);
#endif

  return 0;
}

static int
___mingwthr_remove_key_dtor ( DWORD key )
{
  __mingwthr_key_t *prev_key;
  __mingwthr_key_t *cur_key;

  EnterCriticalSection (&__mingwthr_cs);

  prev_key = NULL;
  cur_key = key_dtor_list;

  while( cur_key != NULL )
  {
     if( cur_key->key == key )
     {
// take key/dtor out of list
        if( prev_key == NULL )
        {
           key_dtor_list = cur_key->next;
        }
        else
        {
           prev_key->next = cur_key->next;
        }

#ifdef DEBUG
        printf ("%s: removing: (%ld)\n", 
                __FUNCTION__, key );
#endif

        free( cur_key );
        break;
     }

     prev_key = cur_key;
     cur_key = cur_key->next;
  }

  LeaveCriticalSection (&__mingwthr_cs);

  return 0;
}

/*
 * __mingwthr_run_key_dtors (void):
 *
 * Callback from DllMain when thread detaches to clean up the key
 * storage. 
 *
 * Note that this does not delete the key itself, but just runs
 * the dtor if the current value are both non-NULL. Note that the
 * keys with NULL dtors are not added by __mingwthr_key_dtor, the
 * only public interface, so we don't need to check. 
 *
 */

void
__mingwthr_run_key_dtors (void)
{
  __mingwthr_key_t *keyp;

#ifdef DEBUG
  printf ("%s: Entering Thread id %ld\n", __FUNCTION__, GetCurrentThreadId() );
#endif

  EnterCriticalSection (&__mingwthr_cs);

  for (keyp = key_dtor_list; keyp; )
  {
     LPVOID value = TlsGetValue (keyp->key);
     if (GetLastError () == ERROR_SUCCESS)
     {
#ifdef DEBUG
        printf ("   (%ld, %x)\n", keyp->key, keyp->dtor);
#endif
        if (value)
           (*keyp->dtor) (value);
     }
#ifdef DEBUG
     else
     {
        printf ("   TlsGetValue FAILED  (%ld, %x)\n", 
                keyp->key, keyp->dtor);
     }
#endif
     keyp = keyp->next;
  }
  
  LeaveCriticalSection (&__mingwthr_cs);

#ifdef DEBUG
  printf ("%s: Exiting Thread id %ld\n", __FUNCTION__, GetCurrentThreadId() );
#endif
}
  
/*
 * __mingwthr_register_key_dtor (DWORD key, void (*dtor) (void *))
 *
 * Public interface called by C++ exception handling mechanism in
 * libgcc (cf: __gthread_key_create).
 *
 */

__declspec(dllexport)
int
__mingwthr_key_dtor (DWORD key, void (*dtor) (void *))
{
  if (dtor)
    {
      return ___mingwthr_add_key_dtor (key, dtor);
    }

  return 0;
}

__declspec(dllexport)
int
__mingwthr_remove_key_dtor (DWORD key )
{
   return ___mingwthr_remove_key_dtor ( key );
}