worker.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #include <pthread.h>
  2. extern "C" {
  3. #include <event2/thread.h>
  4. #include <event2/bufferevent.h>
  5. #include <event2/buffer.h>
  6. #include <event2/event.h>
  7. }
  8. #include <NTL/ZZ_p.h>
  9. #include <vector>
  10. #include <stdio.h>
  11. #include "cudadl.h"
  12. #include "evutils.h"
  13. #include "subproblem.h"
  14. NTL_CLIENT
  15. typedef enum {
  16. WRKCCSTATE_AWAITCMD,
  17. WRKCCSTATE_RDPROBLEM,
  18. WRKCCSTATE_RDDPNODES,
  19. WRKCCSTATE_END
  20. } WrkCCState;
  21. struct WrkControllerConnInfo {
  22. WrkCCState state;
  23. unsigned short num_dpnodes;
  24. WrkControllerConnInfo() : state(WRKCCSTATE_AWAITCMD), num_dpnodes(0) {}
  25. };
  26. typedef enum { WT_NOT_RUNNING, WT_RUNNING, WT_SHOULD_STOP} WTState;
  27. static struct WrkControllerState {
  28. Subproblem *current_problem;
  29. unsigned short num_expected_dpnodes;
  30. vector<struct bufferevent *> dpnodes;
  31. unsigned short num_connected_dpnodes;
  32. WTState worker_thread_state;
  33. pthread_t worker_thread;
  34. WrkControllerState(): current_problem(NULL),
  35. worker_thread_state(WT_NOT_RUNNING) {}
  36. } wrkctrlstate;
  37. // ----- Below this line are the functions running in the worker thread.
  38. // This function is called from inside cuda_dl for each DP it encounters.
  39. // It calls the function named "dpcallback" directly. It would be
  40. // cleaner if this were passed as a function pointer to cuda_dl, but
  41. // that makes nvcc 3.1 segfault. :-p
  42. bool dpcallback(void *cbdata, unsigned short threadId,
  43. unsigned short blockId, unsigned int demux, unsigned int *dpwords,
  44. unsigned int a_0, unsigned int a_1, unsigned int a_2,
  45. unsigned int b_0, unsigned int b_1, unsigned int b_2)
  46. {
  47. struct bufferevent *bev =
  48. wrkctrlstate.dpnodes[demux % wrkctrlstate.num_connected_dpnodes];
  49. unsigned char DPbuf[(WORDS+6)*sizeof(unsigned int)];
  50. memmove(DPbuf, dpwords, WORDS*sizeof(unsigned int));
  51. memmove(DPbuf+WORDS*sizeof(unsigned int), &a_0, sizeof(unsigned int));
  52. memmove(DPbuf+(WORDS+1)*sizeof(unsigned int), &a_1, sizeof(unsigned int));
  53. memmove(DPbuf+(WORDS+2)*sizeof(unsigned int), &a_2, sizeof(unsigned int));
  54. memmove(DPbuf+(WORDS+3)*sizeof(unsigned int), &b_0, sizeof(unsigned int));
  55. memmove(DPbuf+(WORDS+4)*sizeof(unsigned int), &b_1, sizeof(unsigned int));
  56. memmove(DPbuf+(WORDS+5)*sizeof(unsigned int), &b_2, sizeof(unsigned int));
  57. bufferevent_write(bev, DPbuf, (WORDS+6)*sizeof(unsigned int));
  58. // If worker_thread_state changes to WT_SHOULD_STOP, then signal to
  59. // stop computation by returning true. If for some reason, it
  60. // becomes WT_NOT_RUNNING (which it shouldn't), stop as well.
  61. return wrkctrlstate.worker_thread_state != WT_RUNNING;
  62. }
  63. static void *worker_thread_start(void *data)
  64. {
  65. ZZ_p::init(wrkctrlstate.current_problem->modulus);
  66. cuda_dl(to_ZZ_p(wrkctrlstate.current_problem->base),
  67. to_ZZ_p(wrkctrlstate.current_problem->target),
  68. wrkctrlstate.current_problem->order,
  69. wrkctrlstate.current_problem->modulus, NULL);
  70. return NULL;
  71. }
  72. // ----- Above this line are the functions running in the worker thread.
  73. // Below are the functions running in the main (communication) thread.
  74. static void stop_working(void)
  75. {
  76. cerr << "Stopping work\n";
  77. if (wrkctrlstate.worker_thread_state != WT_NOT_RUNNING) {
  78. // Tell the worker thread to stop after its next kernel launch
  79. wrkctrlstate.worker_thread_state = WT_SHOULD_STOP;
  80. pthread_join(wrkctrlstate.worker_thread, NULL);
  81. wrkctrlstate.worker_thread_state = WT_NOT_RUNNING;
  82. }
  83. // Close the connections to the dpnodes
  84. vector<struct bufferevent *>::iterator bevit;
  85. for (bevit = wrkctrlstate.dpnodes.begin();
  86. bevit != wrkctrlstate.dpnodes.end(); ++bevit) {
  87. bufferevent_free(*bevit);
  88. }
  89. wrkctrlstate.dpnodes.clear();
  90. wrkctrlstate.num_connected_dpnodes = 0;
  91. wrkctrlstate.num_expected_dpnodes = 0;
  92. // Careful! Subproblem uses NTL, so we must be sure we're not
  93. // multithreaded at this point.
  94. delete wrkctrlstate.current_problem;
  95. wrkctrlstate.current_problem = NULL;
  96. }
  97. static void start_working(void)
  98. {
  99. cerr << "Starting work\n";
  100. wrkctrlstate.worker_thread_state = WT_RUNNING;
  101. if (pthread_create(&wrkctrlstate.worker_thread, NULL,
  102. worker_thread_start, NULL)) {
  103. wrkctrlstate.worker_thread = WT_NOT_RUNNING;
  104. cerr << "Could not start worker thread\n";
  105. }
  106. }
  107. static void dpconn_event_cb(struct bufferevent *bev, short events,
  108. void *ctx)
  109. {
  110. if (events & BEV_EVENT_CONNECTED) {
  111. // We have successfully connected to the dpnode
  112. bufferevent_enable(bev, EV_WRITE);
  113. ++wrkctrlstate.num_connected_dpnodes;
  114. if (wrkctrlstate.num_connected_dpnodes ==
  115. wrkctrlstate.num_expected_dpnodes) {
  116. start_working();
  117. }
  118. } else if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  119. fprintf(stderr, "Closing connection to dpnode\n");
  120. stop_working();
  121. bufferevent_free(bev);
  122. }
  123. }
  124. static void controllerconn_reader(struct bufferevent *bev, void *ctx)
  125. {
  126. struct evbuffer *input = bufferevent_get_input(bev);
  127. WrkControllerConnInfo *info = (WrkControllerConnInfo *)ctx;
  128. unsigned char cmd[1];
  129. unsigned char subproblem[SUBPROBLEM_DESC_LEN];
  130. while(1) {
  131. size_t len = evbuffer_get_length(input);
  132. switch(info->state) {
  133. case WRKCCSTATE_AWAITCMD:
  134. if (len < 1) return;
  135. bufferevent_read(bev, cmd, 1);
  136. switch(cmd[0]) {
  137. case 'P':
  138. info->state = WRKCCSTATE_RDPROBLEM;
  139. break;
  140. case 'S':
  141. stop_working();
  142. break;
  143. default:
  144. /* Unknown command received */
  145. fprintf(stderr, "Unknown command in "
  146. "controllerconn_reader: %c\n", cmd[0]);
  147. info->state = WRKCCSTATE_END;
  148. break;
  149. }
  150. break;
  151. case WRKCCSTATE_RDPROBLEM:
  152. if (len < SUBPROBLEM_DESC_LEN+2) return;
  153. stop_working();
  154. bufferevent_read(bev, subproblem, SUBPROBLEM_DESC_LEN);
  155. bufferevent_read(bev, &(info->num_dpnodes), 2);
  156. // Careful! Subproblem uses NTL, so we must be sure
  157. // we're not multithreaded at this point.
  158. wrkctrlstate.current_problem = new Subproblem(subproblem);
  159. info->state = WRKCCSTATE_RDDPNODES;
  160. /* FALLTHROUGH */
  161. case WRKCCSTATE_RDDPNODES:
  162. if (len < 6*(info->num_dpnodes)) return;
  163. wrkctrlstate.num_expected_dpnodes = info->num_dpnodes;
  164. {
  165. unsigned short i;
  166. for(i=0;i<info->num_dpnodes;++i) {
  167. unsigned char ipport[6];
  168. bufferevent_read(bev, ipport, 6);
  169. cerr << "Connecting to DPnode " <<
  170. int(ipport[0]) << "." <<
  171. int(ipport[1]) << "." <<
  172. int(ipport[2]) << "." <<
  173. int(ipport[3]) << ":" <<
  174. ((ipport[4] << 8) + ipport[5]) <<
  175. "\n";
  176. struct bufferevent *dpbev = client_create(
  177. bufferevent_get_base(bev), ipport,
  178. dpconn_event_cb, true);
  179. if (dpbev) {
  180. wrkctrlstate.dpnodes.push_back(dpbev);
  181. } else {
  182. stop_working();
  183. }
  184. }
  185. }
  186. info->state = WRKCCSTATE_AWAITCMD;
  187. break;
  188. case WRKCCSTATE_END:
  189. // Shut down
  190. delete info;
  191. event_base_loopbreak(bufferevent_get_base(bev));
  192. bufferevent_free(bev);
  193. return;
  194. }
  195. }
  196. }
  197. static void controllerconn_event_cb(struct bufferevent *bev, short events,
  198. void *ctx)
  199. {
  200. if (events & BEV_EVENT_CONNECTED) {
  201. // We have successfully connected to the controller
  202. char id[1] = { 'W' };
  203. bufferevent_enable(bev, EV_READ|EV_WRITE);
  204. bufferevent_write(bev, id, 1);
  205. bufferevent_setcb(bev, controllerconn_reader, NULL,
  206. controllerconn_event_cb, new WrkControllerConnInfo());
  207. } else if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  208. fprintf(stderr, "Closing connection to controller and exiting\n");
  209. event_base_loopbreak(bufferevent_get_base(bev));
  210. bufferevent_free(bev);
  211. }
  212. }
  213. int main(int argc, char **argv)
  214. {
  215. if (argc != 3) {
  216. fprintf(stderr, "Usage: %s controller_host controller_port\n", argv[0]);
  217. return 1;
  218. }
  219. unsigned short controller_port = strtoul(argv[2], NULL, 10);
  220. evthread_use_pthreads();
  221. return controller_client(argv[1], controller_port,
  222. controllerconn_event_cb, true);
  223. }