worker.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. #include <event2/util.h>
  8. }
  9. #include <NTL/ZZ_p.h>
  10. #include <fstream>
  11. #include <vector>
  12. #include <stdio.h>
  13. #include <errno.h>
  14. #include <signal.h>
  15. #include "cudadl.h"
  16. #include "evutils.h"
  17. #include "subproblem.h"
  18. #include "worker.h"
  19. NTL_CLIENT
  20. #undef VERBOSE
  21. typedef enum {
  22. WRKCCSTATE_AWAITCMD,
  23. WRKCCSTATE_RDPROBLEM,
  24. WRKCCSTATE_RDDPNODES,
  25. WRKCCSTATE_END
  26. } WrkCCState;
  27. struct WrkControllerConnInfo {
  28. WrkCCState state;
  29. unsigned short num_dpnodes;
  30. WrkControllerConnInfo() : state(WRKCCSTATE_AWAITCMD), num_dpnodes(0) {}
  31. };
  32. typedef enum { WT_NOT_RUNNING, WT_RUNNING, WT_SHOULD_STOP} WTState;
  33. static struct WrkControllerState {
  34. Subproblem *current_problem;
  35. unsigned short num_expected_dpnodes;
  36. vector<struct bufferevent *> dpnodes;
  37. unsigned short num_connected_dpnodes;
  38. WTState worker_thread_state;
  39. pthread_t worker_thread;
  40. WrkControllerState(): current_problem(NULL),
  41. worker_thread_state(WT_NOT_RUNNING) {}
  42. } wrkctrlstate;
  43. // ----- Below this line are the functions running in the worker thread.
  44. #if WORDS > 1
  45. #define DEMUXWORD 8
  46. #else
  47. #define DEMUXWORD 7
  48. #endif
  49. // This function is called from inside cuda_dl for each DP it encounters.
  50. // It calls the function named "dpcallback" directly. It would be
  51. // cleaner if this were passed as a function pointer to cuda_dl, but
  52. // that makes nvcc 3.1 segfault. :-p
  53. // dp points to an array of WORDS+7 unsigned ints:
  54. // - 1 word of threadID/blockID
  55. // - WORDS words of the dp value
  56. // - 3 words of a
  57. // - 3 words of b
  58. bool dpcallback(void *cbdata, unsigned int *dpwords)
  59. {
  60. unsigned int demux = dpwords[DEMUXWORD];
  61. struct bufferevent *bev =
  62. wrkctrlstate.dpnodes[demux % wrkctrlstate.num_connected_dpnodes];
  63. bufferevent_write(bev, dpwords+1, (WORDS+6)*sizeof(unsigned int));
  64. // If worker_thread_state changes to WT_SHOULD_STOP, then signal to
  65. // stop computation by returning true. If for some reason, it
  66. // becomes WT_NOT_RUNNING (which it shouldn't), stop as well.
  67. return wrkctrlstate.worker_thread_state != WT_RUNNING;
  68. }
  69. static void *worker_thread_start(void *data)
  70. {
  71. ZZ_p::init(wrkctrlstate.current_problem->modulus);
  72. cuda_dl(to_ZZ_p(wrkctrlstate.current_problem->base),
  73. to_ZZ_p(wrkctrlstate.current_problem->target),
  74. wrkctrlstate.current_problem->order,
  75. wrkctrlstate.current_problem->modulus,
  76. wrkctrlstate.current_problem->dpfreq, NULL);
  77. return NULL;
  78. }
  79. // ----- Above this line are the functions running in the worker thread.
  80. // Below are the functions running in the main (communication) thread.
  81. static void stop_working(void)
  82. {
  83. #ifdef VERBOSE
  84. cerr << "Stopping work\n";
  85. #endif
  86. if (wrkctrlstate.worker_thread_state != WT_NOT_RUNNING) {
  87. // Tell the worker thread to stop after its next kernel launch
  88. wrkctrlstate.worker_thread_state = WT_SHOULD_STOP;
  89. pthread_join(wrkctrlstate.worker_thread, NULL);
  90. wrkctrlstate.worker_thread_state = WT_NOT_RUNNING;
  91. }
  92. // Close the connections to the dpnodes
  93. vector<struct bufferevent *>::iterator bevit;
  94. for (bevit = wrkctrlstate.dpnodes.begin();
  95. bevit != wrkctrlstate.dpnodes.end(); ++bevit) {
  96. #ifdef VERBOSE
  97. cerr << "Closing connection to " << *bevit << "\n";
  98. #endif
  99. bufferevent_free(*bevit);
  100. }
  101. wrkctrlstate.dpnodes.clear();
  102. wrkctrlstate.num_connected_dpnodes = 0;
  103. wrkctrlstate.num_expected_dpnodes = 0;
  104. // Careful! Subproblem uses NTL, so we must be sure we're not
  105. // multithreaded at this point.
  106. delete wrkctrlstate.current_problem;
  107. wrkctrlstate.current_problem = NULL;
  108. }
  109. static void start_working(void)
  110. {
  111. #ifdef VERBOSE
  112. cerr << "Starting work\n";
  113. #endif
  114. cout << "Subproblem " << wrkctrlstate.current_problem->problemid << "\n";
  115. cout.flush();
  116. wrkctrlstate.worker_thread_state = WT_RUNNING;
  117. if (pthread_create(&wrkctrlstate.worker_thread, NULL,
  118. worker_thread_start, NULL)) {
  119. wrkctrlstate.worker_thread = WT_NOT_RUNNING;
  120. cerr << "Could not start worker thread\n";
  121. }
  122. }
  123. static void dpconn_event_cb(struct bufferevent *bev, short events,
  124. void *ctx)
  125. {
  126. if (events & BEV_EVENT_CONNECTED) {
  127. // We have successfully connected to the dpnode
  128. #ifdef VERBOSE
  129. cerr << "Connection established to dpnode " << bev << "\n";
  130. #endif
  131. bufferevent_enable(bev, EV_WRITE);
  132. ++wrkctrlstate.num_connected_dpnodes;
  133. if (wrkctrlstate.num_connected_dpnodes ==
  134. wrkctrlstate.num_expected_dpnodes) {
  135. start_working();
  136. }
  137. } else if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  138. #ifdef VERBOSE
  139. cerr << "Closing connection to dpnode " << bev << " ";
  140. if (events & BEV_EVENT_EOF) {
  141. cerr << "EOF";
  142. }
  143. if (events & BEV_EVENT_ERROR) {
  144. cerr << "ERR (" << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()) << ")";
  145. }
  146. cerr << "\n";
  147. #endif
  148. stop_working();
  149. }
  150. }
  151. static void controllerconn_reader(struct bufferevent *bev, void *ctx)
  152. {
  153. struct evbuffer *input = bufferevent_get_input(bev);
  154. WrkControllerConnInfo *info = (WrkControllerConnInfo *)ctx;
  155. unsigned char cmd[1];
  156. unsigned char subproblem[SUBPROBLEM_DESC_LEN];
  157. while(1) {
  158. size_t len = evbuffer_get_length(input);
  159. switch(info->state) {
  160. case WRKCCSTATE_AWAITCMD:
  161. if (len < 1) return;
  162. bufferevent_read(bev, cmd, 1);
  163. #ifdef VERBOSE
  164. cerr << "Command " << cmd[0] << " received\n";
  165. #endif
  166. switch(cmd[0]) {
  167. case 'P':
  168. info->state = WRKCCSTATE_RDPROBLEM;
  169. break;
  170. case 'S':
  171. stop_working();
  172. break;
  173. default:
  174. /* Unknown command received */
  175. fprintf(stderr, "Unknown command in "
  176. "controllerconn_reader: %c\n", cmd[0]);
  177. info->state = WRKCCSTATE_END;
  178. break;
  179. }
  180. break;
  181. case WRKCCSTATE_RDPROBLEM:
  182. if (len < SUBPROBLEM_DESC_LEN+2) return;
  183. stop_working();
  184. bufferevent_read(bev, subproblem, SUBPROBLEM_DESC_LEN);
  185. bufferevent_read(bev, &(info->num_dpnodes), 2);
  186. // Careful! Subproblem uses NTL, so we must be sure
  187. // we're not multithreaded at this point.
  188. wrkctrlstate.current_problem = new Subproblem(subproblem);
  189. info->state = WRKCCSTATE_RDDPNODES;
  190. /* FALLTHROUGH */
  191. case WRKCCSTATE_RDDPNODES:
  192. if (len < 6*(info->num_dpnodes)) return;
  193. wrkctrlstate.num_expected_dpnodes = info->num_dpnodes;
  194. {
  195. unsigned short i;
  196. for(i=0;i<info->num_dpnodes;++i) {
  197. unsigned char ipport[6];
  198. bufferevent_read(bev, ipport, 6);
  199. #ifdef VERBOSE
  200. cerr << "Connecting to DPnode " <<
  201. int(ipport[0]) << "." <<
  202. int(ipport[1]) << "." <<
  203. int(ipport[2]) << "." <<
  204. int(ipport[3]) << ":" <<
  205. ((ipport[4] << 8) + ipport[5]) <<
  206. "\n";
  207. #endif
  208. struct bufferevent *dpbev = client_create(
  209. bufferevent_get_base(bev), ipport,
  210. dpconn_event_cb, true);
  211. #ifdef VERBOSE
  212. cerr << "Starting connection to dpnode " << dpbev << "\n";
  213. #endif
  214. if (dpbev) {
  215. wrkctrlstate.dpnodes.push_back(dpbev);
  216. } else {
  217. stop_working();
  218. }
  219. }
  220. }
  221. info->state = WRKCCSTATE_AWAITCMD;
  222. break;
  223. case WRKCCSTATE_END:
  224. // Shut down
  225. delete info;
  226. event_base_loopbreak(bufferevent_get_base(bev));
  227. #ifdef VERBOSE
  228. cerr << "Closing connection to " << bev << "\n";
  229. #endif
  230. bufferevent_free(bev);
  231. return;
  232. }
  233. }
  234. }
  235. static void controllerconn_event_cb(struct bufferevent *bev, short events,
  236. void *ctx)
  237. {
  238. if (events & BEV_EVENT_CONNECTED) {
  239. // We have successfully connected to the controller
  240. char id[1] = { 'W' };
  241. bufferevent_enable(bev, EV_READ|EV_WRITE);
  242. bufferevent_write(bev, id, 1);
  243. bufferevent_setcb(bev, controllerconn_reader, NULL,
  244. controllerconn_event_cb, new WrkControllerConnInfo());
  245. } else if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  246. fprintf(stderr, "Closing connection to controller and exiting\n");
  247. event_base_loopbreak(bufferevent_get_base(bev));
  248. #ifdef VERBOSE
  249. cerr << "Closing connection to " << bev << "\n";
  250. #endif
  251. bufferevent_free(bev);
  252. }
  253. }
  254. int worker_main(const char *controller_host, unsigned short controller_port)
  255. {
  256. // Initialize the prng with some randomness from the kernel
  257. unsigned char randbuf[1024];
  258. ifstream urand("/dev/urandom");
  259. urand.read((char *)randbuf, sizeof(randbuf));
  260. urand.close();
  261. ZZ randzz = ZZFromBytes(randbuf, sizeof(randbuf));
  262. SetSeed(randzz);
  263. evthread_use_pthreads();
  264. signal(SIGPIPE, SIG_IGN);
  265. return controller_client(controller_host, controller_port,
  266. controllerconn_event_cb, true);
  267. }