1#include "stlport_prefix.h"
2
3#if defined(__unix) && defined(__GNUC__)
4
5#ifdef __FreeBSD__
6#  include <osreldate.h>
7#endif
8
9#if (defined(__FreeBSD__) && (__FreeBSD_version < 503001)) || defined(__sun) || defined (__hpux)
10/* Note: __cxa_finalize and __cxa_atexit present in libc in FreeBSD 5.3 */
11
12#include <stdlib.h>
13#include <stdio.h>
14#include <pthread.h>
15
16/* __asm__ (".symver " "__cxa_finalize" "," "__cxa_finalize" "@" "STLPORT_5_0_0"); */
17/* __asm__ (".symver " "__cxa_finalize" "," "__cxa_finalize" "@@" "STLPORT_5_0_0"); */
18
19/* Not atomic! */
20/* But we can use static mutexes here: I hope that performance issue isn't very
21   significant on unloading (for only few calls, ~10) - ptr */
22
23/*
24#define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \
25  ({ __typeof (mem) __gmemp = (mem);                                  \
26     __typeof (*mem) __gnewval = (newval);                            \
27                                                                      \
28     *__gmemp == (oldval) ? (*__gmemp = __gnewval, 0) : 1; })
29*/
30
31enum {
32  ef_free, /* `ef_free' MUST be zero!  */
33  ef_us,
34  ef_on,
35  ef_at,
36  ef_cxa
37};
38
39struct exit_function
40{
41  /* `flavour' should be of type of the `enum' above but since we need
42     this element in an atomic operation we have to use `long int'.  */
43  long int flavor;
44  union {
45    void (*at)(void);
46    struct {
47      void (*fn)(int status, void *arg);
48      void *arg;
49    } on;
50    struct {
51      void (*fn)(void *arg, int status);
52      void *arg;
53      void *dso_handle;
54    } cxa;
55  } func;
56};
57
58struct exit_function_list
59{
60  struct exit_function_list *next;
61  size_t idx;
62  struct exit_function fns[32];
63};
64
65struct exit_function *__new_exitfn (void);
66
67/* Register a function to be called by exit or when a shared library
68   is unloaded.  This function is only called from code generated by
69   the C++ compiler.  */
70int __cxa_atexit(void (*func)(void *), void *arg, void *d)
71{
72  struct exit_function *new = __new_exitfn ();
73
74  if ( new == NULL )
75    return -1;
76
77  new->flavor = ef_cxa;
78  new->func.cxa.fn = (void (*) (void *, int)) func;
79  new->func.cxa.arg = arg;
80  new->func.cxa.dso_handle = d;
81  return 0;
82}
83
84
85/* We change global data, so we need locking.  */
86#ifdef __linux__
87static pthread_mutex_t lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
88#endif
89/* #ifdef __FreeBSD__ */
90#if 0
91static pthread_mutex_t lock =
92  { PTHREAD_MUTEX_RECURSIVE /* PTHREAD_MUTEX_DEFAULT */, PTHREAD_PRIO_NONE, {NULL,NULL},
93    NULL, { NULL }, /* MUTEX_FLAGS_PRIVATE */ 0x1, 0, 0, 0, {NULL, NULL},
94    { 0, 0, 0, 0 } };
95#endif
96#ifdef __sun
97static pthread_mutex_t lock =
98  {{0, 0, 0, PTHREAD_MUTEX_RECURSIVE, _MUTEX_MAGIC}, {{{0}}}, 0};
99#endif
100#ifdef __hpux
101static pthread_mutex_t lock = PTHREAD_MUTEX_RECURSIVE_INITIALIZER_NP;
102#  ifdef __ia64
103void *__dso_handle = (void *) &__dso_handle;
104#  endif
105#endif
106
107
108static struct exit_function_list initial;
109struct exit_function_list *__exit_funcs = &initial;
110
111struct exit_function *__new_exitfn(void)
112{
113  struct exit_function_list *l;
114  size_t i = 0;
115
116#ifndef __FreeBSD__
117  pthread_mutex_lock( &lock );
118#endif
119
120  for (l = __exit_funcs; l != NULL; l = l->next) {
121    for (i = 0; i < l->idx; ++i)
122      if (l->fns[i].flavor == ef_free)
123        break;
124    if ( i < l->idx )
125      break;
126
127    if (l->idx < sizeof (l->fns) / sizeof (l->fns[0])) {
128      i = l->idx++;
129      break;
130    }
131  }
132
133  if (l == NULL) {
134    l = (struct exit_function_list *)malloc( sizeof(struct exit_function_list) );
135    if (l != NULL) {
136      l->next = __exit_funcs;
137      __exit_funcs = l;
138
139      l->idx = 1;
140      i = 0;
141    }
142  }
143
144  /* Mark entry as used, but we don't know the flavor now.  */
145  if ( l != NULL )
146    l->fns[i].flavor = ef_us;
147
148#ifndef __FreeBSD__
149  pthread_mutex_unlock( &lock );
150#endif
151
152  return l == NULL ? NULL : &l->fns[i];
153}
154
155/* If D is non-NULL, call all functions registered with `__cxa_atexit'
156   with the same dso handle.  Otherwise, if D is NULL, call all of the
157   registered handlers.  */
158
159/*
160 * Note, that original __cxa_finalize don't use lock, but use __exit_funcs
161 * i.e. global data.
162 */
163void __cxa_finalize(void *d)
164{
165  struct exit_function_list *funcs;
166
167#ifndef __FreeBSD__
168  pthread_mutex_lock( &lock );
169#endif
170
171  for (funcs = __exit_funcs; funcs; funcs = funcs->next) {
172    struct exit_function *f;
173
174    for (f = &funcs->fns[funcs->idx - 1]; f >= &funcs->fns[0]; --f) {
175      if ( (d == NULL || d == f->func.cxa.dso_handle) && (f->flavor == ef_cxa) ) {
176        f->flavor = ef_free;
177        (*f->func.cxa.fn) (f->func.cxa.arg, 0);
178      }
179    }
180  }
181
182  /* Remove the registered fork handlers.  We do not have to
183     unregister anything if the program is going to terminate anyway.  */
184#ifdef UNREGISTER_ATFORK
185  if (d != NULL)
186    UNREGISTER_ATFORK (d);
187#endif
188#ifndef __FreeBSD__
189  pthread_mutex_unlock( &lock );
190#endif
191}
192
193/* __asm__ (".symver " "__cxa_finalize" "," "__cxa_finalize" "@@" "STLPORT_5_0_0"); */
194/* void __cxa_finalize(void *d) __attribute__ ((weak)); */
195
196#endif /* OS name */
197#endif /* __unix */
198
199