Advertisement
Guest User

Mutex

a guest
Dec 12th, 2018
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.52 KB | None | 0 0
  1. #include <stdlib.h> /*malloc, free*/
  2. #include <stdio.h> /*printf*/
  3. #include <ucontext.h> /*context functionality*/
  4. #include <assert.h> /*assert*/
  5. #include <signal.h>
  6. #include <sys/time.h>
  7. #include "green.h"
  8.  
  9. #define PERIOD 100
  10. #define FALSE 0
  11. #define TRUE 1
  12. #define STACK_SIZE 4096
  13. #define MAX 1000000
  14.  
  15. static ucontext_t main_cntx = {0};
  16. static green_t main_green = {&main_cntx, NULL, NULL, NULL, NULL, FALSE};
  17. static green_t* running = &main_green;
  18. static sigset_t block;
  19.  
  20. static void init () __attribute__ ((constructor));
  21. void timer_handler(int sig);
  22.  
  23. void init () {
  24. getcontext (&main_cntx);
  25. sigemptyset(&block);
  26. sigaddset(&block, SIGVTALRM);
  27.  
  28. struct sigaction act = {0};
  29. struct timeval interval;
  30. struct itimerval period;
  31.  
  32. act.sa_handler = timer_handler;
  33. assert(sigaction(SIGVTALRM, &act, NULL) == 0);
  34.  
  35. interval.tv_sec = 0;
  36. interval.tv_usec = PERIOD;
  37. period.it_interval = interval;
  38. period.it_value = interval;
  39. setitimer(ITIMER_VIRTUAL, &period, NULL);
  40. }
  41.  
  42. struct Queue {
  43. green_t* front;
  44. green_t* end;
  45. };
  46. struct Queue* queue;
  47.  
  48. // Function to add a key to queue
  49. void myEnQueue(green_t* new) {
  50.  
  51. if (queue->end == NULL) {
  52. queue->front = queue->end = new;
  53. return;
  54. }
  55. queue->end->next = new;
  56. queue->end = new;
  57. }
  58.  
  59. // Function to remove a key from queue
  60. struct green_t* myDeQueue() {
  61. if (queue->front == NULL) {
  62. return NULL;
  63. }
  64. //printf("1: queue->front = %p \n", queue->front);
  65. struct green_t* temp = queue->front;
  66. //temp->next = NULL;
  67. queue->front = queue->front->next;
  68. temp->next = NULL;
  69. if (queue->front == NULL) {
  70. queue->end = NULL;
  71. } else {
  72. //printf("2: queue->front = %p \n", queue->front);
  73. }
  74. return temp;
  75. }
  76.  
  77. void removeFromReadyList(green_t* remove) {
  78. struct green_t* temp = queue->front;
  79. struct green_t* afterTemp = temp->next;
  80. if (temp == remove) {
  81. queue->front = queue->front->next;
  82. //printf("if (temp == remove), queue->front = %p \n", queue->front);
  83. return;
  84. }
  85. while (afterTemp != remove) {
  86. temp = temp->next;
  87. afterTemp = afterTemp->next;
  88. }
  89. if (afterTemp != NULL) {
  90. temp->next = afterTemp->next;
  91. }
  92. }
  93.  
  94. int green_mutex_init(green_mutex_t *mutex) {
  95. mutex->taken = FALSE;
  96. mutex->susp = NULL;
  97. }
  98.  
  99. int green_mutex_lock(green_mutex_t *mutex) {
  100. // block timer interrupt
  101. sigprocmask(SIG_BLOCK, &block, NULL);
  102.  
  103. green_t *susp = running;
  104. while(mutex->taken) {
  105. // suspend the running thread. Should I have a susp list?
  106. // add susp to susp queue
  107. if (mutex->susp == NULL){
  108. mutex->susp = running;
  109. } else {
  110. //I want to make sure that we add it at the end of the queue.
  111. green_t* current = mutex->susp;
  112. while(current->next != NULL){
  113. if (current->next == susp) {
  114. break;
  115. }
  116. //printf(" current->next != %p, current = %p \n", current->next, current);
  117. current = current->next;
  118. }
  119. current->next = running;
  120. running->next = NULL;
  121. }
  122.  
  123. // select the next thread for execution
  124. struct green_t* next = myDeQueue();
  125.  
  126. // find the next thread
  127. running = next;
  128. swapcontext(susp->context, next->context);
  129. }
  130. // take the lock. This is enough, the caller function will wait until it gets back,
  131. // and do whatever with the locks.
  132. mutex->taken = TRUE;
  133. //mutex->susp = running;
  134.  
  135. // unblock
  136. sigprocmask(SIG_UNBLOCK, &block, NULL);
  137. return 0;
  138. }
  139. int green_mutex_unlock(green_mutex_t *mutex) {
  140. // block timer interrupt
  141. sigprocmask(SIG_BLOCK, &block, NULL);
  142. green_t *susp = running;
  143. green_t* suspended = mutex->susp;
  144. // move suspended threads to ready queue
  145. while (suspended != NULL){
  146. if (suspended == susp) {
  147. break;
  148. }
  149. //printf(" mutex->susp != %p \n", mutex->susp);
  150. myEnQueue(suspended);
  151. if (suspended == suspended->next) {
  152. break;
  153. }
  154. suspended = suspended->next;
  155. //mutex->susp = NULL;
  156. }
  157.  
  158. mutex->taken = FALSE;
  159. mutex->susp = NULL;
  160. // unblock
  161. sigprocmask(SIG_UNBLOCK, &block, NULL);
  162. return 0;
  163. }
  164.  
  165. /*
  166. Initialize a green condition variable.
  167. */
  168. void green_cond_init(green_cond_t* cond) {
  169. cond = malloc(sizeof(struct green_cond_t));
  170. }
  171.  
  172. /*
  173. Suspend the current thread
  174. on the condition.
  175. */
  176. void green_cond_wait(green_cond_t* cond) {
  177. sigprocmask(SIG_BLOCK, &block, NULL);
  178.  
  179. green_t* this = running;
  180. if (cond->end == NULL) {
  181. cond->front = cond->end = this;
  182. return;
  183. }
  184. cond->end->next = this;
  185. cond->end = this;
  186. //printf("this = %p \n", this);
  187. //run next
  188. struct green_t* next = myDeQueue();
  189. //printf("next = %p \n", next);
  190. running = next;
  191. swapcontext (this->context, next->context);
  192. sigprocmask(SIG_UNBLOCK, &block, NULL);
  193. }
  194.  
  195. /*
  196. Move the first suspended
  197. thread to the ready cond.
  198. */
  199. void green_cond_signal(green_cond_t* cond) {
  200. sigprocmask(SIG_BLOCK, &block, NULL);
  201. if (cond->front == NULL) {
  202. return;
  203. }
  204. struct green_t* temp = cond->front;
  205. cond->front = cond->front->next;
  206. if (cond->front == NULL) {
  207. cond->end = NULL;
  208. }
  209. //printf("temp = %p \n", temp);
  210. //printf("cond->front = %p \n", cond->front);
  211. myEnQueue(temp);
  212. sigprocmask(SIG_UNBLOCK, &block, NULL);
  213. //return temp;
  214. }
  215.  
  216. /*
  217. This function is responsible for calling the function provided by the user.
  218.  
  219. This function will do (execute) two things:
  220. 1. Start the execution of the real function (the function provided by the user).
  221. 2. After returning from the call, terminate the thread.
  222.  
  223. The tricky part is what to do when the called function returns.
  224. The program should check if there is a thread waiting for its termination,
  225. if so place it in the ready queue.
  226. The stack that was allocated, and the space for the context,
  227. should be returned to the memory management system;
  228. the thread is now a zombie process.
  229. There should be a thread in the ready queue
  230. so the program select the first and schedule it for execution.
  231. */
  232. void green_thread() {
  233. sigprocmask(SIG_UNBLOCK, &block, NULL);
  234. green_t* this = running;
  235.  
  236. (*this->function)(this->arg);
  237. // place waiting (joining) thread in ready queue
  238. if (this == NULL) {
  239. //printf("this == NULL \n");
  240. }
  241. sigprocmask(SIG_BLOCK, &block, NULL);
  242. struct green_t* j = this->join;
  243. if (j != NULL) {
  244. //printf("j = %p \n", j);
  245. myEnQueue(j);
  246. //swapcontext (this->context, this->join->context);
  247. }
  248. printf ("thread %d is done \n", *(int*)this->arg);
  249. //printf("this = %p \n", this);
  250. // free alocated memory structures, i.e. free the stack.
  251. removeFromReadyList(this);
  252. free(this->context->uc_stack.ss_sp);
  253. free(this->context);
  254. // we're a zombie
  255. this->zombie = TRUE;
  256. //printf("Zombie \n");
  257. // find the next thread to run
  258. struct green_t* next = myDeQueue();
  259. if (next != NULL) {
  260. //printf("next = %p \n", next);
  261. running = next;
  262. setcontext (next->context);
  263. }
  264. //sigprocmask(SIG_UNBLOCK, &block, NULL);
  265. //printf("after next != NULL \n");
  266. //setcontext (this->context);
  267. }
  268.  
  269. /*create a green thread*/
  270. int green_create (green_t* new, void* (*function)(void*), void* arg) {
  271. // sigprocmask(SIG_BLOCK, &block, NULL);
  272. ucontext_t* cntx = (ucontext_t *)malloc(sizeof(ucontext_t));
  273. getcontext (cntx);
  274.  
  275. void* stack = malloc (STACK_SIZE);
  276.  
  277. cntx->uc_stack.ss_sp = stack;
  278. cntx->uc_stack.ss_size = STACK_SIZE;
  279.  
  280. makecontext (cntx, green_thread, 0);
  281. new->context = cntx;
  282. new->function = function;
  283. new->arg = arg;
  284. new->next = NULL;
  285. new->join = NULL;
  286. new->zombie = FALSE;
  287. // add new to the ready queue
  288. sigprocmask(SIG_BLOCK, &block, NULL);
  289. myEnQueue(new);
  290. sigprocmask(SIG_UNBLOCK, &block, NULL);
  291. return 0;
  292. }
  293.  
  294. /*
  295. Suspends the current thread and selects a new thread for execution.
  296.  
  297. This function will simply put the running thread last in the ready queue
  298. and then select the first thread from the queue as the next thread to run.
  299.  
  300. The call to swapcontext() will do a context switch. It will save the
  301. current state in susp->context and continue execution from next->context.
  302. Note that when the suspended thread is scheduled, it will continue the execution
  303. from exactly this point.
  304. */
  305. int green_yield () {
  306. sigprocmask(SIG_BLOCK, &block, NULL);
  307. green_t* susp = running;
  308. // add susp to ready queue
  309. myEnQueue(susp);
  310. // select the next thread for execution
  311. struct green_t* next = myDeQueue();
  312.  
  313. running = next;
  314. swapcontext (susp->context, next->context);
  315. sigprocmask(SIG_UNBLOCK, &block, NULL);
  316. return 0;
  317. }
  318.  
  319. /*
  320. The current thread is suspended waiting for a specific thread to terminate.
  321.  
  322. The join operation will wait for a thread to terminate. The program therefore add
  323. the waiting thread to the join field to the argument and select another
  324. thread for execution.
  325. If the thread has already terminated we can of course continue
  326. as if nothing happened.
  327.  
  328. The program could allow several threads to wait for the same thread.
  329. */
  330. int green_join (green_t* thread) {
  331.  
  332. if (thread->zombie) {
  333. return 0;
  334. }
  335. green_t* susp = running;
  336. sigprocmask(SIG_BLOCK, &block, NULL);
  337. // add to waiting threads
  338. //sigprocmask(SIG_BLOCK, &block, NULL);
  339. thread->join = susp;
  340. //printf ("susp = %p, susp->join = %p\n", susp, susp->join);
  341. // select the next thread for execution
  342. struct green_t* next = myDeQueue();
  343.  
  344. running = next;
  345. //printf ("gonna swap\n");
  346. swapcontext (susp->context, next->context);
  347. sigprocmask(SIG_UNBLOCK, &block, NULL);
  348. //printf ("back in join\n");
  349. return 0;
  350. }
  351. void timer_handler(int sig) {
  352. sigprocmask(SIG_BLOCK, &block, NULL);
  353. green_t * susp = running;
  354.  
  355. // add the running to the ready queue. Also have to
  356. myEnQueue(susp);
  357.  
  358. // find the next thread for execution
  359. green_t* next = myDeQueue();
  360.  
  361. running = next;
  362. swapcontext(susp->context, next->context);
  363. sigprocmask(SIG_UNBLOCK, &block, NULL);
  364. }
  365.  
  366. /*
  367. --------------------------------------Testing--------------------------------------
  368. */
  369.  
  370. //new
  371. int flag = 0;
  372. green_cond_t cond;
  373. green_mutex_t mutex;
  374. int numThreads = 10;
  375. int counter = 0;
  376. //end new
  377.  
  378. void* test(void* arg) {
  379. int id = *(int*)arg;
  380. int loop = 4;
  381.  
  382. while(loop > 0) {
  383. if(flag == id) {
  384. printf("thread %d: %d \n", id, loop);
  385. printf("Work \n");
  386. loop--;
  387. flag = (id + 1) % 2;
  388. green_cond_signal(&cond);
  389. } else {
  390. //printf("thread %d: %d ", id, loop);
  391. //printf("Wait \n");
  392. green_cond_wait(&cond);
  393. }
  394. }
  395. //printf("while out \n");
  396. }
  397.  
  398. void* testSharedResource(void* arg) {
  399. int id = *(int*)arg;
  400. for(int i = 0; i < 1000000; i++) {
  401. green_mutex_lock(&mutex);
  402. int temp = counter;
  403.  
  404. /*
  405. int dummy = 0;
  406. if(temp > 1337) // waste time between read and write
  407. dummy++;
  408. */
  409. printf(" count: %d, thread: %d, ", counter, id);
  410. temp++;
  411. counter = temp;
  412. green_mutex_unlock(&mutex);
  413. }
  414. }
  415.  
  416. void *test_timer(void *arg){
  417. int i = *(int*)arg;
  418. int loop = MAX;
  419. while(loop>0){
  420. loop--;
  421. counter++;
  422. printf(" count: %d, thread: %d\n",counter, i);
  423. }
  424. }
  425.  
  426. int main () {
  427. queue = malloc(sizeof *queue);
  428. /*green_t g0, g1;
  429. myEnQueue(&g0);
  430. printf ("queue.front %p\n", queue->end);
  431. myEnQueue(&g1);
  432. printf ("queue.front %p\n", queue->end);*/
  433. //end own
  434. green_t g0, g1;
  435. //green_cond_t cond;
  436. //printf ("cond = %p \n", cond.front);
  437. green_cond_init(&cond);
  438. green_mutex_init(&mutex);
  439. //printf ("cond = %p \n", cond.front);
  440. //green_cond_wait(&cond);
  441. //green_cond_signal(&cond);
  442. //printf ("cond = %p \n", cond.front);
  443. //printf ("g0 = %p, g1 = %p \n", &g0, &g1);
  444.  
  445. int a0 = 0;
  446. int a1 = 1;
  447.  
  448.  
  449. green_create(&g0, testSharedResource, &a0);
  450. green_create(&g1, testSharedResource, &a1);
  451.  
  452. green_join(&g0);
  453. green_join(&g1);
  454. //printf ("between\n");
  455.  
  456.  
  457. printf ("done\n");
  458. return 0;
  459. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement