1 /*
2  *   Copyright (c) 2014 - 2019 Oleh Kulykov <info@resident.name>
3  *
4  *   Permission is hereby granted, free of charge, to any person obtaining a copy
5  *   of this software and associated documentation files (the "Software"), to deal
6  *   in the Software without restriction, including without limitation the rights
7  *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  *   copies of the Software, and to permit persons to whom the Software is
9  *   furnished to do so, subject to the following conditions:
10  *
11  *   The above copyright notice and this permission notice shall be included in
12  *   all copies or substantial portions of the Software.
13  *
14  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  *   THE SOFTWARE.
21  */
22 
23 
24 #include "librws.h"
25 #include "rws_thread.h"
26 #include "rws_memory.h"
27 #include "rws_common.h"
28 
29 #include <assert.h>
30 #include <pthread.h>
31 #include <semaphore.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 
35 struct rws_thread_struct {
36 	rws_thread_funct thread_function;
37 	void * user_object;
38 	pthread_t thread;
39 };
40 
41 typedef struct _rws_threads_joiner_struct {
42 	rws_thread thread;
43 	rws_mutex mutex;
44 } _rws_threads_joiner;
45 
46 static _rws_threads_joiner * _threads_joiner = NULL;
rws_threads_joiner_clean(void)47 static void rws_threads_joiner_clean(void) { // private
48 	rws_thread t = _threads_joiner->thread;
49 	void * r = NULL;
50 
51 	if (!t) {
52 		return;
53 	}
54 	_threads_joiner->thread = NULL;
55 	pthread_join(t->thread, &r);
56 	assert(r == NULL);
57 
58 	rws_free(t);
59 }
60 
rws_threads_joiner_add(rws_thread thread)61 static void rws_threads_joiner_add(rws_thread thread) { // public
62 	rws_mutex_lock(_threads_joiner->mutex);
63 	rws_threads_joiner_clean();
64 	_threads_joiner->thread = thread;
65 	rws_mutex_unlock(_threads_joiner->mutex);
66 }
67 
rws_threads_joiner_create_ifneed(void)68 static void rws_threads_joiner_create_ifneed(void) {
69 	if (_threads_joiner) {
70 		return;
71 	}
72 	_threads_joiner = (_rws_threads_joiner *)rws_malloc_zero(sizeof(_rws_threads_joiner));
73 	_threads_joiner->mutex = rws_mutex_create_recursive();
74 }
75 
rws_thread_func_priv(void * some_pointer)76 static void * rws_thread_func_priv(void * some_pointer) {
77 	rws_thread t = (rws_thread)some_pointer;
78 	t->thread_function(t->user_object);
79 	rws_threads_joiner_add(t);
80 
81 	return NULL;
82 }
83 
rws_thread_create(rws_thread_funct thread_function,void * user_object)84 rws_thread rws_thread_create(rws_thread_funct thread_function, void * user_object) {
85 	rws_thread t = NULL;
86 	int res = -1;
87 	pthread_attr_t attr;
88 
89 	if (!thread_function) {
90 		return NULL;
91 	}
92 	rws_threads_joiner_create_ifneed();
93 	t = (rws_thread)rws_malloc_zero(sizeof(struct rws_thread_struct));
94 	t->user_object = user_object;
95 	t->thread_function = thread_function;
96 	if (pthread_attr_init(&attr) == 0) {
97         pthread_attr_setstacksize(&attr, 16 * 1024);
98         struct sched_param sched;
99 #ifndef _AMLOGIC_
100         sched.sched_priority = 32;
101 #else
102         sched.sched_priority = 3;
103 #endif
104         pthread_attr_setschedparam(&attr, &sched);
105         //if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0) {
106    		    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == 0) {
107 				res = pthread_create(&t->thread, &attr, &rws_thread_func_priv, (void *)t);
108 				pthread_setname_np(t->thread, "GnWebsoc");
109 			}
110 		//}
111 		pthread_attr_destroy(&attr);
112 	}
113 	assert(res == 0);
114 	return t;
115 }
116 
rws_thread_sleep(const unsigned int millisec)117 void rws_thread_sleep(const unsigned int millisec) {
118 	usleep(millisec * 1000); // 1s = 1'000'000 microsec.
119 }
120 
rws_mutex_create_recursive(void)121 rws_mutex rws_mutex_create_recursive(void) {
122 	pthread_mutex_t * mutex = (pthread_mutex_t *)rws_malloc_zero(sizeof(pthread_mutex_t));
123 	int res = -1;
124 	pthread_mutexattr_t attr;
125 	if (pthread_mutexattr_init(&attr) == 0) {
126 		if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) == 0) {
127 			res = pthread_mutex_init(mutex, &attr);
128 		}
129 		pthread_mutexattr_destroy(&attr);
130 	}
131 	assert(res == 0);
132 	return mutex;
133 }
134 
rws_mutex_lock(rws_mutex mutex)135 void rws_mutex_lock(rws_mutex mutex) {
136 	if (mutex) {
137 		pthread_mutex_lock((pthread_mutex_t *)mutex);
138 	}
139 }
140 
rws_mutex_unlock(rws_mutex mutex)141 void rws_mutex_unlock(rws_mutex mutex) {
142 	if (mutex) {
143 		pthread_mutex_unlock((pthread_mutex_t *)mutex);
144 	}
145 }
146 
rws_mutex_delete(rws_mutex mutex)147 void rws_mutex_delete(rws_mutex mutex) {
148 	if (mutex) {
149 		pthread_mutex_destroy((pthread_mutex_t *)mutex);
150 		rws_free(mutex);
151 	}
152 }
153 
rws_sem_create(void)154 rws_sem rws_sem_create(void)
155 {
156 	int ret  = -1;
157 	sem_t *sem = (sem_t *) rws_malloc_zero(sizeof(sem_t));
158 
159 	if (sem)
160 	   ret = sem_init(sem, 0, 0);
161 
162 	assert(ret == 0);
163 
164 	return sem;
165 }
166 
rws_sem_delete(rws_sem sem)167 void rws_sem_delete(rws_sem sem)
168 {
169 	if (sem) {
170         sem_destroy(sem);
171         rws_free(sem);
172 	}
173 }
174 
rws_sem_signal(rws_sem sem)175 void rws_sem_signal(rws_sem sem)
176 {
177     if (sem) {
178     	sem_post(sem);
179     }
180 }
181 
rws_sem_wait(rws_sem sem,unsigned int timeout_ms)182 int rws_sem_wait(rws_sem sem, unsigned int timeout_ms)
183 {
184     int ret = -1;
185 
186 	if (sem) {
187 		if (timeout_ms == RWS_WAIT_FOREVER) {
188 			ret = sem_wait(sem);
189 		} else {
190 			struct timespec abs_timeout;
191             struct timeval tv;
192             gettimeofday(&tv, NULL);
193 
194 			abs_timeout.tv_sec = tv.tv_sec + timeout_ms / 1000;
195 			abs_timeout.tv_nsec = tv.tv_usec * 1000 + (timeout_ms % 1000) * 1000000;
196 			ret = sem_timedwait(sem, &abs_timeout);
197 		}
198 	}
199 
200 	return ret;
201 }
202