You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
4.4 KiB

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2013, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the COPYING file in this distribution for license details.
  7. * ----------------------------------------------------------------------
  8. */
  9. #include <assert.h>
  10. #include <pthread.h>
  11. #include "libcork/core/allocator.h"
  12. #include "libcork/core/error.h"
  13. #include "libcork/core/types.h"
  14. #include "libcork/ds/buffer.h"
  15. #include "libcork/threads/basics.h"
  16. /*-----------------------------------------------------------------------
  17. * Current thread
  18. */
  19. static volatile cork_thread_id last_thread_descriptor = 0;
  20. struct cork_thread {
  21. const char *name;
  22. cork_thread_id id;
  23. pthread_t thread_id;
  24. struct cork_thread_body *body;
  25. cork_error error_code;
  26. struct cork_buffer error_message;
  27. bool started;
  28. bool joined;
  29. };
  30. struct cork_thread_descriptor {
  31. struct cork_thread *current_thread;
  32. cork_thread_id id;
  33. };
  34. cork_tls(struct cork_thread_descriptor, cork_thread_descriptor);
  35. struct cork_thread *
  36. cork_current_thread_get(void)
  37. {
  38. struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
  39. return desc->current_thread;
  40. }
  41. cork_thread_id
  42. cork_current_thread_get_id(void)
  43. {
  44. struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
  45. if (CORK_UNLIKELY(desc->id == 0)) {
  46. if (desc->current_thread == NULL) {
  47. desc->id = cork_uint_atomic_add(&last_thread_descriptor, 1);
  48. } else {
  49. desc->id = desc->current_thread->id;
  50. }
  51. }
  52. return desc->id;
  53. }
  54. /*-----------------------------------------------------------------------
  55. * Threads
  56. */
  57. struct cork_thread *
  58. cork_thread_new(const char *name, struct cork_thread_body *body)
  59. {
  60. struct cork_thread *self = cork_new(struct cork_thread);
  61. self->name = cork_strdup(name);
  62. self->id = cork_uint_atomic_add(&last_thread_descriptor, 1);
  63. self->body = body;
  64. self->error_code = CORK_ERROR_NONE;
  65. cork_buffer_init(&self->error_message);
  66. self->started = false;
  67. self->joined = false;
  68. return self;
  69. }
  70. static void
  71. cork_thread_free_private(struct cork_thread *self)
  72. {
  73. cork_strfree(self->name);
  74. cork_thread_body_free(self->body);
  75. cork_buffer_done(&self->error_message);
  76. free(self);
  77. }
  78. void
  79. cork_thread_free(struct cork_thread *self)
  80. {
  81. assert(!self->started);
  82. cork_thread_free_private(self);
  83. }
  84. const char *
  85. cork_thread_get_name(struct cork_thread *self)
  86. {
  87. return self->name;
  88. }
  89. cork_thread_id
  90. cork_thread_get_id(struct cork_thread *self)
  91. {
  92. return self->id;
  93. }
  94. static void *
  95. cork_thread_pthread_run(void *vself)
  96. {
  97. int rc;
  98. struct cork_thread *self = vself;
  99. struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
  100. desc->current_thread = self;
  101. desc->id = self->id;
  102. rc = cork_thread_body_run(self->body);
  103. /* If an error occurred in the body of the thread, save the error into the
  104. * cork_thread object so that we can propagate that error when some calls
  105. * cork_thread_join. */
  106. if (CORK_UNLIKELY(rc != 0)) {
  107. if (CORK_LIKELY(cork_error_occurred())) {
  108. self->error_code = cork_error_code();
  109. cork_buffer_set_string(&self->error_message, cork_error_message());
  110. } else {
  111. self->error_code = CORK_UNKNOWN_ERROR;
  112. cork_buffer_set_string(&self->error_message, "Unknown error");
  113. }
  114. }
  115. return NULL;
  116. }
  117. int
  118. cork_thread_start(struct cork_thread *self)
  119. {
  120. int rc;
  121. pthread_t thread_id;
  122. assert(!self->started);
  123. rc = pthread_create(&thread_id, NULL, cork_thread_pthread_run, self);
  124. if (CORK_UNLIKELY(rc != 0)) {
  125. cork_system_error_set_explicit(rc);
  126. return -1;
  127. }
  128. self->thread_id = thread_id;
  129. self->started = true;
  130. return 0;
  131. }
  132. int
  133. cork_thread_join(struct cork_thread *self)
  134. {
  135. int rc;
  136. assert(self->started && !self->joined);
  137. rc = pthread_join(self->thread_id, NULL);
  138. if (CORK_UNLIKELY(rc != 0)) {
  139. cork_system_error_set_explicit(rc);
  140. cork_thread_free_private(self);
  141. return -1;
  142. }
  143. if (CORK_UNLIKELY(self->error_code != CORK_ERROR_NONE)) {
  144. cork_error_set_printf
  145. (self->error_code, "Error from thread %s: %s",
  146. self->name, (char *) self->error_message.buf);
  147. cork_thread_free_private(self);
  148. return -1;
  149. }
  150. cork_thread_free_private(self);
  151. return 0;
  152. }