/** A thread that actually recevices data from remote worker.
 * @return If the action succeed, it will return 0. Otherwise, socket descriptor.
 */
void* TransferServer::transfer_server_thread(void) {
  static int32_t ret = 0;
  LOG_DEBUG("transfer_server_thread started - %u", pthread_self());
  try {
    serverfd = CreateBindedSocket(start_port_range_, end_port_range_, &server_socket_port);
  } catch (...) {
    LOG_ERROR("transfer_server_thread - fail to open/bind a socket\n");
    return reinterpret_cast<void*>(&ret);
  }
  LOG_DEBUG("transfer_server_thread socket bind complete. binded port: %u %d", pthread_self(), server_socket_port);
  int32_t new_fd = -1;
  struct sockaddr_in their_addr;
  socklen_t sin_size = sizeof(their_addr);
  ret = listen(serverfd, MAX_CONN);
  if (ret < 0) {
    fprintf(stderr, "listen failure\n");
    sem_post(&server_ready);
    return reinterpret_cast<void*>(&ret);
  }

  sem_post(&server_ready);

  // TODO(shivaram): Check if it is okay if we only wait for one connection
  // Also this is blocking ! We need to use a select / libevent call
  // here
  new_fd = accept(serverfd, (struct sockaddr *) &their_addr,
                  &sin_size);
  if (new_fd < 0) {
    ret = new_fd;
    return reinterpret_cast<void*>(&ret);
  }

  uint32_t sz;
  uint32_t size_bytes_read = 0;
  uint64_t recv_t = 0;

#ifdef PROFILE_TRANSFER_TIME
  clock_gettime(CLOCK_REALTIME, &start_t);
#endif

  bytes_fetched_ += (size_);
  atomicio(read, new_fd, dest_, size_);

#ifdef PROFILE_TRANSFER_TIME
  clock_gettime(CLOCK_REALTIME, &end_t);
  recv_t = diff_clocktime(&start_t, &end_t);
#endif

  close(new_fd);
  close(serverfd);
  ret = 0;
  return &ret;
}
void PrestoMasterHandler::Run(context_t* ctx, int port_start, int port_end) {  
  bool worker_abort_called = false;
  int port_num = -1;
  socket_t* sock;
  bool bind_succeed = true;
  try {
    sock = CreateBindedSocket(port_start, port_end, ctx, &port_num);
  } catch (...) {
    bind_succeed = false;
  }
  presto_master_->SetMasterPortNum(port_num);
  presto_master_->WorkerInfoSemaPost();

  if (bind_succeed == false) {
    fprintf(stderr, "MasterHandler socket bind error. Check port number: %d\n", port_num);
    return;
  }
  MasterRequest master_req;  
  try {
    while (true) {
      message_t request;
      boost::this_thread::interruption_point();  // check if interrupted
      sock->recv(&request);
      boost::this_thread::interruption_point();  // check if interrupted
      master_req.Clear();
      master_req.ParseFromArray(request.data(), request.size());
      switch (master_req.type()) {
        case MasterRequest::SHUTDOWN:
          LOG_INFO("PrestoMasterHanlder Shutdown is called");          
          delete sock;
          return;
        case MasterRequest::HELLOREPLY:
          {
            thread thr(boost::bind(&PrestoMaster::HandleHelloReply,
                                   presto_master_,
                                   master_req.helloreply()));
          }
          break;
        case MasterRequest::NEWUPDATE:
          {
            NewUpdateRequest nur = master_req.newupdate();
            if (scheduler_->IsValidWorker(server_to_string(*nur.mutable_location())) == false) {
              break;
            }
#ifdef MULTITHREADED_SCHEDULER
            thread thr(boost::bind(&PrestoMasterHandler::NewUpdate,
                                   this,
                                   master_req.newupdate()));
#else
            NewUpdate(master_req.newupdate());
#endif
          }
          break;
        case MasterRequest::TASKDONE:
          {
            TaskDoneRequest tdr = master_req.taskdone();
            if (scheduler_->IsValidWorker(server_to_string(*tdr.mutable_location())) == false) {
              break;
            }
            
#ifdef MULTITHREADED_SCHEDULER
            thread thr(boost::bind(
                                   &PrestoMasterHandler::HandleTaskDone,
                                   this,
                                   master_req.taskdone()));
#else
            bool ret = HandleTaskDone(master_req.taskdone());
            // If an error happens during HandlingTaskDone message, shutdown the whole session
            if (ret == false) {
	      LOG_ERROR("Error during processing task at worker (HandleTaskDone). Shutting down.");
	      fprintf(stderr, 
                    "Error while processing task at worker."
		      " We will shutdown the whole session. Use Ctrl-C to return to R console.\n");
              thread thr(boost::bind(&PrestoMaster::Shutdown,
                                     presto_master_));
              thr.detach();
              return;
            }
#endif
          }
          break;
        case MasterRequest::WORKERABORT:
          {
            WorkerAbortRequest war = master_req.workerabort();
            if (scheduler_->IsValidWorker(server_to_string(*war.mutable_location())) == false) {
              break;
            }
            // if a shutdown message is already processed,
            // ignore further messages
            if (worker_abort_called == true) {
              break;
            }
            worker_abort_called = true;
            fprintf(stderr, 
                    "\n%sWorker aborted\n%s\n"
                    "We will shutdown the whole session.\n",
                    exception_prefix.c_str(), 
                    master_req.workerabort().reason().c_str());
            ostringstream msg;
            msg << exception_prefix << "Worker aborted. "<< master_req.workerabort().reason().c_str() << "\nWe will shutdown the whole session";
            LOG_INFO(msg.str());
            thread thr(boost::bind(&PrestoMaster::Shutdown,
                                   presto_master_));
            thr.detach();
          }
          break;

        default:
          {
            LOG_ERROR("Unknown message received from Worker. Possible reasons - Check the correctness of the configuration - Either Hostname or Port of the Master and Workers has to be different");
            fprintf(stderr, "Unknown message type received - Possible reasons\n"
            "Check the correctness of the configuration - Either Hostname or Port of the Master and Workers has to be different\n");
	  }
      }
    }
  }catch(boost::thread_interrupted const& ) {
    LOG_INFO("PrestoMasterHandler - interrupted.");
    delete sock;
    return;
  } catch (zmq::error_t err) {
    //LOG_INFO("PrestoMasterHandler - zmq error_t exception received. Teminate MasterHandler");
    delete sock;
    return;
  }
  delete sock;
}