worker.cc 7.5 KB

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