1 // Copyright 2015 The Emscripten Authors.  All rights reserved.
2 // Emscripten is available under two separate licenses, the MIT license and the
3 // University of Illinois/NCSA Open Source License.  Both these licenses can be
4 // found in the LICENSE file.
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <pthread.h>
9 #include <emscripten.h>
10 #include <emscripten/threading.h>
11 #include <unistd.h>
12 
13 #define NUM_THREADS 8
14 
15 int numThreadsToCreateTotal = 50;
16 
17 pthread_t thread[NUM_THREADS] = {};
18 
19 volatile int counter = 0; // Shared data
20 pthread_mutex_t lock;
21 
sleep(int msecs)22 void sleep(int msecs)
23 {
24 	// Test two different variants of sleeping to verify
25 	// against bug https://bugzilla.mozilla.org/show_bug.cgi?id=1131757
26 #ifdef SPINLOCK_TEST
27 	double t0 = emscripten_get_now();
28 	double t1 = t0 + (double)msecs;
29 	while(emscripten_get_now() < t1)
30 		;
31 #else
32 	usleep(msecs*1000);
33 #endif
34 }
ThreadMain(void * arg)35 void *ThreadMain(void *arg)
36 {
37 	pthread_mutex_lock(&lock);
38 	int c = counter;
39 	sleep(100); // Create contention on the lock.
40 	++c;
41 	counter = c;
42 	pthread_mutex_unlock(&lock);
43 	pthread_exit(0);
44 }
45 
CreateThread(int i,int n)46 void CreateThread(int i, int n)
47 {
48 	pthread_attr_t attr;
49 	pthread_attr_init(&attr);
50 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
51 	pthread_attr_setstacksize(&attr, 4*1024);
52 	int rc = pthread_create(&thread[i], &attr, ThreadMain, 0);
53 	if (rc != 0 || thread[i] == 0)
54 		printf("Failed to create thread!\n");
55 	pthread_attr_destroy(&attr);
56 }
57 
58 int threadNum = 0;
WaitToJoin(double time,void * userData)59 EM_BOOL WaitToJoin(double time, void *userData)
60 {
61 	int threadsRunning = 0;
62 	// Join all threads.
63 	for(int i = 0; i < NUM_THREADS; ++i)
64 	{
65 		if (thread[i])
66 		{
67 			void *status;
68 			int rc = pthread_join(thread[i], &status);
69 			if (rc == 0)
70 			{
71 				thread[i] = 0;
72 				if (threadNum < numThreadsToCreateTotal)
73 				{
74 					CreateThread(i, threadNum++);
75 					++threadsRunning;
76 				}
77 			}
78 			else
79 				++threadsRunning;
80 		}
81 	}
82 	if (!threadsRunning)
83 	{
84 		if (counter == numThreadsToCreateTotal)
85 			EM_ASM(console.log('All threads finished. Counter = ' + $0 + ' as expected.'), counter);
86 		else
87 			EM_ASM(console.error('All threads finished, but counter = ' + $0 + ' != ' + $1 + '!'), counter, numThreadsToCreateTotal);
88 #ifdef REPORT_RESULT
89 		REPORT_RESULT(counter);
90 #endif
91 		return EM_FALSE;
92 	}
93 	return EM_TRUE;
94 }
95 
main()96 int main()
97 {
98 	pthread_mutexattr_t attr;
99 	pthread_mutexattr_init(&attr);
100 	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
101 	pthread_mutex_init(&lock, &attr);
102 
103 	pthread_mutex_lock(&lock);
104 	pthread_mutex_unlock(&lock);
105 
106 	if (emscripten_has_threading_support()) {
107 		// Create new threads in parallel.
108 		for(int i = 0; i < NUM_THREADS; ++i)
109 			CreateThread(i, threadNum++);
110 
111 		emscripten_set_timeout_loop(WaitToJoin, 100, 0);
112 	} else {
113 #ifdef REPORT_RESULT
114 		REPORT_RESULT(50);
115 #endif
116 	}
117 }
118