1 #ifndef _REENTRANT
2 #error "Recompile libcwd with --enable-threading"
3 #endif
4 //#include "sys.h"
5 #include "../include/sys.h"
6 #include "threads_debug.h"
7 #include <vector>
8 #include <iostream>
9 
10 using libcwd::_private_::mutex_tct;
11 using libcwd::_private_::rwlock_tct;
12 using libcwd::_private_::test_instance0;
13 
14 template<class TYPE>
15   class foo_tct {
16   public:
foo_tct(void)17     foo_tct(void)
18     {
19       Dout(dc::notice, "Creating " << type_info_of<foo_tct<TYPE> >().demangled_name()
20           << " with pthread_self() = " << pthread_self());
21     }
~foo_tct(void)22     ~foo_tct(void)
23     {
24       Dout(dc::notice, "Destroying " << type_info_of<foo_tct<TYPE> >().demangled_name()
25           << " with pthread_self() = " << pthread_self());
26     }
27   };
28 
29 template<class TYPE>
test(TYPE &)30   inline typename TYPE::foo_ct test(TYPE&)
31   {
32     static foo_tct<std::vector<typename TYPE::foo_ct> > foo;
33     static typename TYPE::foo_ct dummy;
34     return dummy;
35   }
36 
37 int const number_of_threads = 4;
38 static int volatile state_thread[number_of_threads];
39 
40 class A {
41 public:
42   typedef int foo_ct;
43 };
44 
45 class B {
46 public:
47   typedef float foo_ct;
48 };
49 
50 class C {
51 public:
52   typedef A const* foo_ct;
53 };
54 
in_the_middle(int my_id)55 char const* in_the_middle(int my_id)
56 {
57   if (my_id != number_of_threads - 1)
58   {
59     Dout(dc::notice, my_id << ": waiting for thread " << my_id + 1);
60     while(state_thread[my_id + 1] == 0);
61   }
62   state_thread[my_id] = 1;
63   A a; B b; C c;
64   if (my_id == 1)
65     test(a);
66   else if (my_id == 2)
67     test(b);
68   else
69     test(c);
70   return "in the middle of a Dout.";
71 }
72 
73 class TSD {
74 public:
75   TSD(void);
76   ~TSD();
77 };
78 
TSD(void)79 TSD::TSD(void)
80 {
81   Dout(dc::notice, "Calling TSD(), this = " << (void*)this << '.');
82 }
83 
~TSD()84 TSD::~TSD()
85 {
86   Dout(dc::notice, "Calling ~TSD(), this is " << (void*)this << ".  From "
87       << location_ct((char*)__builtin_return_address(0) + builtin_return_address_offset));
88 }
89 
90 static pthread_key_t key;
91 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
destroy(void * arg)92 static void destroy(void* arg) { TSD* tsd = reinterpret_cast<TSD*>(arg); delete tsd; }
key_alloc()93 static void key_alloc() { pthread_key_create(&key, destroy); }
94 
thread_function(void *)95 void* thread_function(void*)
96 {
97   static int thread_counter = -1;
98 
99   pthread_once(&key_once, key_alloc);
100   pthread_setspecific(key, new TSD);
101 
102   // Set Thread Specific on/off flags of the debug channels.
103   ForAllDebugChannels( if (!debugChannel.is_on()) debugChannel.on(); );
104   // And for the debug object.
105   Debug( libcw_do.on() );
106 
107   // Serialize incrementation.
108   int my_id;
109   LIBCWD_DEFER_CANCEL;
110   mutex_tct<test_instance0>::lock();
111   my_id = ++thread_counter;
112   mutex_tct<test_instance0>::unlock();
113   LIBCWD_RESTORE_CANCEL;
114 
115   state_thread[my_id] = 0;
116   Dout(dc::notice, my_id << ": Entering thread " << pthread_self());
117 
118   // Get TSD.
119   TSD& tsd(*reinterpret_cast<TSD*>(pthread_getspecific(key)));
120   Dout(dc::notice, my_id << ": tsd is " << (void*)&tsd << '.');
121 
122   Dout(dc::notice, my_id << ": Here we are " << in_the_middle(my_id));
123 
124   return NULL;
125 }
126 
127 pthread_mutex_t cout_lock;
128 
main(void)129 int main(void)
130 {
131   Debug( check_configuration() );
132 #if CWDEBUG_ALLOC
133   libcwd::make_all_allocations_invisible_except(NULL);
134 #endif
135   Debug( libcw_do.on() );
136   Debug( libcw_do.set_ostream(&std::cout, &cout_lock) );
137 
138   ForAllDebugChannels( if (!debugChannel.is_on()) debugChannel.on(); );
139   Debug( list_channels_on(libcw_do) );
140 
141 #if CWDEBUG_DEBUGT
142   // This is necessary because we use test_instance0 as rwlock and we got three levels deep.
143   Dout(dc::fatal, "Please use a libcwd_r that was compiled without --enable-debugt for this test.");
144 #endif
145 
146   mutex_tct<test_instance0>::initialize();
147   rwlock_tct<test_instance0>::initialize();
148 
149   // Test if rwlocks allows multiple read locks but only one write lock.
150   LIBCWD_DEFER_CANCEL;
151   rwlock_tct<test_instance0>::wrlock();
152     LIBCWD_ASSERT( !rwlock_tct<test_instance0>::trywrlock() );
153     LIBCWD_ASSERT( !rwlock_tct<test_instance0>::tryrdlock() );
154   rwlock_tct<test_instance0>::wrunlock();
155   LIBCWD_RESTORE_CANCEL;
156 
157   LIBCWD_DEFER_CANCEL;
158   rwlock_tct<test_instance0>::rdlock();
159     LIBCWD_ASSERT( !rwlock_tct<test_instance0>::trywrlock() );
160     LIBCWD_ASSERT( rwlock_tct<test_instance0>::tryrdlock() && (rwlock_tct<test_instance0>::rdunlock(), true) );
161     rwlock_tct<test_instance0>::rdlock();
162       LIBCWD_ASSERT( !rwlock_tct<test_instance0>::trywrlock() );
163       LIBCWD_ASSERT( rwlock_tct<test_instance0>::tryrdlock() && (rwlock_tct<test_instance0>::rdunlock(), true) );
164     rwlock_tct<test_instance0>::rdunlock();
165     LIBCWD_ASSERT( !rwlock_tct<test_instance0>::trywrlock() );
166     LIBCWD_ASSERT( rwlock_tct<test_instance0>::tryrdlock() && (rwlock_tct<test_instance0>::rdunlock(), true) );
167   rwlock_tct<test_instance0>::rdunlock();
168   LIBCWD_ASSERT( rwlock_tct<test_instance0>::tryrdlock() && (rwlock_tct<test_instance0>::rdunlock(), true) );
169   LIBCWD_ASSERT( rwlock_tct<test_instance0>::trywrlock() && (rwlock_tct<test_instance0>::wrunlock(), true) );
170   LIBCWD_RESTORE_CANCEL;
171 
172   // Now test that a mutex allows only one lock.
173   LIBCWD_DEFER_CANCEL;
174   mutex_tct<test_instance0>::lock();
175   LIBCWD_ASSERT( !mutex_tct<test_instance0>::trylock() );
176   mutex_tct<test_instance0>::unlock();
177   LIBCWD_RESTORE_CANCEL;
178 
179   pthread_t thread_id[number_of_threads];
180   for (int i = 0; i < number_of_threads; ++i)
181     pthread_create(&thread_id[i], NULL, thread_function, NULL);
182 
183   for (int i = 0; i < number_of_threads; ++i)
184   {
185     void* status;
186     pthread_join(thread_id[i], &status);
187     Dout(dc::notice, "Thread " << thread_id[i] << " returned with status " << status << '.');
188   }
189 
190   return 0;
191 }
192