1 // Hide VTK_DEPRECATED_IN_9_1_0() warnings for this class.
2 #define VTK_DEPRECATION_LEVEL 0
3 
4 #include "vtkConditionVariable.h"
5 #include "vtkMultiThreader.h"
6 #include "vtksys/SystemTools.hxx"
7 
8 #include <atomic>
9 #include <cstdlib>
10 
11 struct vtkThreadUserData_t
12 {
13   vtkMutexLock* Lock;
14   vtkConditionVariable* Condition;
15   std::atomic<int32_t> Done;
16   int NumberOfWorkers;
17 };
18 using vtkThreadUserData = struct vtkThreadUserData_t;
19 
vtkTestCondVarThread(void * arg)20 VTK_THREAD_RETURN_TYPE vtkTestCondVarThread(void* arg)
21 {
22   int threadId = static_cast<vtkMultiThreader::ThreadInfo*>(arg)->ThreadID;
23   int threadCount = static_cast<vtkMultiThreader::ThreadInfo*>(arg)->NumberOfThreads;
24   vtkThreadUserData* td =
25     static_cast<vtkThreadUserData*>(static_cast<vtkMultiThreader::ThreadInfo*>(arg)->UserData);
26   if (td)
27   {
28     if (threadId == 0)
29     {
30       td->Lock->Lock();
31       td->Done = 0;
32       cout << "Thread " << (threadId + 1) << " of " << threadCount << " initializing.\n";
33       cout.flush();
34       td->Lock->Unlock();
35 
36       int i;
37       for (i = 0; i < 2 * threadCount; ++i)
38       {
39         td->Lock->Lock();
40         cout << "Signaling (count " << i << ")...\n";
41         cout.flush();
42         td->Lock->Unlock();
43         td->Condition->Signal();
44 
45         // sleep( 1 );
46       }
47 
48       i = 0;
49       int currNumWorkers = 0;
50       do
51       {
52         td->Lock->Lock();
53         td->Done = 1;
54         cout << "Broadcasting...\n";
55         cout.flush();
56         currNumWorkers = td->NumberOfWorkers;
57         td->Lock->Unlock();
58         td->Condition->Broadcast();
59         vtksys::SystemTools::Delay(200); // 0.2 s between broadcasts
60       } while (currNumWorkers > 0 && (i++ < 1000));
61       if (i >= 1000)
62       {
63         exit(2);
64       }
65     }
66     else
67     {
68       // Wait for thread 0 to initialize... Ugly but effective
69       bool done = false;
70       do
71       {
72         td->Lock->Lock();
73         if (td->Done)
74         {
75           done = true;
76           td->Lock->Unlock();
77         }
78         else
79         {
80           td->Lock->Unlock();
81           vtksys::SystemTools::Delay(200); // 0.2 s between checking
82         }
83       } while (!done);
84 
85       // Wait for the condition and then note we were signaled.
86       // This part looks like a Hansen Monitor:
87       // ref: http://www.cs.utexas.edu/users/lorenzo/corsi/cs372h/07S/notes/Lecture12.pdf (page
88       // 2/5), code on Tradeoff slide.
89 
90       td->Lock->Lock();
91       while (td->Done <= 0)
92       {
93         cout << " Thread " << (threadId + 1) << " waiting.\n";
94         cout.flush();
95         // Wait() performs an Unlock internally.
96         td->Condition->Wait(td->Lock);
97         // Once Wait() returns, the lock is locked again.
98         cout << " Thread " << (threadId + 1) << " responded.\n";
99         cout.flush();
100       }
101       --td->NumberOfWorkers;
102       td->Lock->Unlock();
103     }
104 
105     td->Lock->Lock();
106     cout << "  Thread " << (threadId + 1) << " of " << threadCount << " exiting.\n";
107     cout.flush();
108     td->Lock->Unlock();
109   }
110   else
111   {
112     cout << "No thread data!\n";
113     cout << "  Thread " << (threadId + 1) << " of " << threadCount << " exiting.\n";
114     cout.flush();
115   }
116 
117   return VTK_THREAD_RETURN_VALUE;
118 }
119 
TestConditionVariable(int,char * [])120 int TestConditionVariable(int, char*[])
121 {
122   vtkMultiThreader* threader = vtkMultiThreader::New();
123   int numThreads = threader->GetNumberOfThreads();
124 
125   vtkThreadUserData data;
126   data.Lock = vtkMutexLock::New();
127   data.Condition = vtkConditionVariable::New();
128   data.Done = -1;
129   data.NumberOfWorkers = numThreads - 1;
130 
131   threader->SetNumberOfThreads(numThreads);
132   threader->SetSingleMethod(vtkTestCondVarThread, &data);
133   threader->SingleMethodExecute();
134 
135   cout << "Done with threader.\n";
136   cout.flush();
137 
138   vtkIndent indent;
139   indent = indent.GetNextIndent();
140   data.Condition->PrintSelf(cout, indent);
141 
142   data.Lock->Delete();
143   data.Condition->Delete();
144   threader->Delete();
145   return 0;
146 }
147