/thread/UserThread.cpp

http://github.com/dennis-gemini/tests · C++ · 199 lines · 167 code · 30 blank · 2 comment · 28 complexity · 26189a3d493f4d4ad8b08066a003bae2 MD5 · raw file

  1. #include <ucontext.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <error.h>
  6. #include <string.h>
  7. #define DEFAULT_STACK_SIZE (10 * 1024)
  8. #define ERROR(msg...) error_at_line(-1, errno, __FILE__, __LINE__, ##msg)
  9. class UserThread
  10. {
  11. public:
  12. static void
  13. schedule()
  14. {
  15. if(getcontext(&scheduler) == -1) {
  16. ERROR("getcontext");
  17. }
  18. if(current == NULL) {
  19. current = head;
  20. } else {
  21. current = current->next;
  22. }
  23. if(current) {
  24. printf("switching to %s\n", current->getName());
  25. if(setcontext(&current->context) == -1) {
  26. ERROR("setcontext");
  27. }
  28. } else {
  29. printf("no current thread, exiting scheduler loop...\n");
  30. }
  31. }
  32. protected:
  33. UserThread(size_t stackSize = DEFAULT_STACK_SIZE):
  34. prev(NULL), next(NULL)
  35. {
  36. stackBase = (unsigned char*) calloc(stackSize, 1);
  37. if(stackBase == NULL) {
  38. ERROR("calloc");
  39. }
  40. this->stackSize = stackSize;
  41. link();
  42. }
  43. virtual
  44. ~UserThread()
  45. {
  46. free(stackBase);
  47. }
  48. virtual void
  49. run()
  50. {
  51. }
  52. virtual const char*
  53. getName() const
  54. { return "<noname>";
  55. }
  56. void
  57. idle()
  58. {
  59. if(swapcontext(&context, &scheduler) == -1) {
  60. ERROR("swapcontext");
  61. }
  62. }
  63. private:
  64. static void
  65. threadRoutine(UserThread* self)
  66. {
  67. if(self) {
  68. self->run();
  69. self->unlink();
  70. }
  71. }
  72. void
  73. link()
  74. {
  75. if(head) {
  76. this->prev = head->prev;
  77. this->next = head;
  78. this->prev->next = this;
  79. this->next->prev = this;
  80. } else {
  81. this->prev = this;
  82. this->next = this;
  83. }
  84. head = this;
  85. if(getcontext(&context) == -1) {
  86. ERROR("getcontext");
  87. }
  88. context.uc_stack.ss_sp = stackBase;
  89. context.uc_stack.ss_size = stackSize;
  90. context.uc_link = &scheduler;
  91. makecontext(&context, (void(*)()) &threadRoutine, 1, this);
  92. }
  93. void
  94. unlink()
  95. {
  96. if(prev && next) {
  97. if(prev == this) {
  98. head = NULL;
  99. current = NULL;
  100. } else {
  101. prev->next = next;
  102. next->prev = prev;
  103. if(head == this) {
  104. head = next;
  105. }
  106. if(current == this) {
  107. current = prev;
  108. }
  109. }
  110. }
  111. }
  112. static UserThread* head;
  113. static UserThread* current;
  114. static ucontext_t scheduler;
  115. UserThread* prev;
  116. UserThread* next;
  117. ucontext_t context;
  118. size_t stackSize;
  119. unsigned char* stackBase;
  120. };
  121. UserThread* UserThread::head = NULL;
  122. UserThread* UserThread::current = NULL;
  123. ucontext_t UserThread::scheduler;
  124. ///////////////////////////////////////////////////////////////////
  125. class Worker: public UserThread
  126. {
  127. int num;
  128. int cur;
  129. char* name;
  130. public:
  131. Worker(int num): num(num), cur(-1)
  132. {
  133. char buf[256];
  134. snprintf(buf, sizeof(buf), "Worker[%d]", num);
  135. name = strdup(buf);
  136. }
  137. virtual
  138. ~Worker()
  139. {
  140. free(name);
  141. }
  142. protected:
  143. virtual void
  144. run()
  145. {
  146. for(cur = 0; cur < 5; cur++) {
  147. printf("Worker[%d]: %d\n", num, cur);
  148. idle();
  149. }
  150. printf("Worker[%d]: exited.\n", num);
  151. }
  152. virtual const char*
  153. getName() const
  154. {
  155. return name;
  156. }
  157. };
  158. int
  159. main(int argc, char** argv)
  160. {
  161. Worker worker1(1);
  162. Worker worker2(2);
  163. Worker worker3(3);
  164. Worker worker4(4);
  165. Worker worker5(5);
  166. UserThread::schedule();
  167. return 0;
  168. }
  169. /* vim:set ts=8 sw=8 foldmethod=syntax foldlevel=1 nu: */