//接受命令。//参数:// cmd_,要接收的命令的buffer。// timeout_,等待超时时间。int zmq::mailbox_t::recv(command_t *cmd_,int timeout_){//先尝试直接读取命令,如果读到了命令则直接返回。//如果读取失败说明当前mailbox中没有未处理的命令,那么把状态设置为不活跃。// Try to get the command straight away.if(active){if(cpipe.read(cmd_))return0;// If there are no more commands available, switch into passive state.
active =false;}//等待信号,如果有信号到达说明有命令到达了mailbox。(函数会阻塞在此)// Wait for signal from the command sender.int rc = signaler.wait(timeout_);if(rc ==-1){errno_assert(errno == EAGAIN || errno == EINTR);return-1;}//收到信号说明有命令要处理。此时把mailbox状态设置为活跃,//然后读取命令到指定buffer中。// Receive the signal.
rc = signaler.recv_failable();if(rc ==-1){errno_assert(errno == EAGAIN);return-1;}// Switch into active state.
active =true;// Get a command.constbool ok = cpipe.read(cmd_);zmq_assert(ok);return0;}
mailbox_t::~mailbox_t ()
//析构函数。//此处用了一个空行为锁保证了不会在其他线程send()的时候释放对象,**是一个好的方法。**//其他线程send()的时候会加锁,因此析构函数会阻塞在lock()操作,只有等到没有send()操作的时候//析构函数才能继续进行下去。
zmq::mailbox_t::~mailbox_t(){// TODO: Retrieve and deallocate commands inside the cpipe.// Work around problem that other threads might still be in our// send() method, by waiting on the mutex before disappearing.
sync.lock();
sync.unlock();}
singnaler_t类
classsignaler_t{public:signaler_t();//构造函数。~signaler_t();//析构函数。
fd_t get_fd()const;//获得读描述符。voidsend();//发送信号。intwait(int timeout_);//等待信号。voidrecv();//接收信号,如果出错程序退出。intrecv_failable();//接收信号,如果出错程序退出。private:// Creates a pair of file descriptors that will be used// to pass the signals.staticintmake_fdpair(fd_t *r_, fd_t *w_);//创建r/w描述符。// Underlying write & read file descriptor// Will be -1 if we exceeded number of available handles
fd_t w;//w描述符,用于发送信号。
fd_t r;//r描述符,用于接收信号。//声明类的拷贝构造函数及拷贝赋值操作符为私有。// Disable copying of signaler_t object.signaler_t(const signaler_t&);const signaler_t &operator=(const signaler_t&);}
signaler_t()
//构造函数。
zmq::signaler_t::signaler_t(){// Create the socketpair for signaling.if(make_fdpair(&r,&w)==0){unblock_socket(w);unblock_socket(r);}#ifdefHAVE_FORK
pid =getpid();#endif}
send()
//发信号。void zmq::signaler_t::send(){#ifdefined HAVE_FORKif(unlikely(pid !=getpid())){//printf("Child process %d signaler_t::send returning without sending #1\n", getpid());return;// do not send anything in forked child context}#endif#ifdefined ZMQ_HAVE_EVENTFDconstuint64_t inc =1;
ssize_t sz =write(w,&inc,sizeof(inc));//向eventfd写输入,默认不使用eventfd。errno_assert(sz ==sizeof(inc));#elseunsignedchar dummy =0;while(true){
ssize_t nbytes =::send(w,&dummy,sizeof(dummy),0);//发数据到w描述符。if(unlikely(nbytes ==-1&& errno == EINTR))continue;#ifdefined(HAVE_FORK)if(unlikely(pid !=getpid())){//printf("Child process %d signaler_t::send returning without sending #2\n", getpid());
errno = EINTR;break;}#endifzmq_assert(nbytes ==sizeof dummy);break;}#endif}
//收信号,如果发生错误程序退出。void zmq::signaler_t::recv(){// Attempt to read a signal.#ifdefined ZMQ_HAVE_EVENTFDuint64_t dummy;
ssize_t sz =read(r,&dummy,sizeof(dummy));//从socket中读。errno_assert(sz ==sizeof(dummy));// If we accidentally grabbed the next signal(s) along with the current// one, return it back to the eventfd object.//一个信号只增加1,如果为>1的数字说明收到了多个信号,这样需要减去1,把剩余值重新存储进去。if(unlikely(dummy >1)){constuint64_t inc = dummy -1;
ssize_t sz2 =write(w,&inc,sizeof(inc));//重新写回值。errno_assert(sz2 ==sizeof(inc));return;}zmq_assert(dummy ==1);#elseunsignedchar dummy;
ssize_t nbytes =::recv(r,&dummy,sizeof(dummy),0);//从socket中收信号。errno_assert(nbytes >=0);zmq_assert(nbytes ==sizeof(dummy));zmq_assert(dummy ==0);#endif}
//生成用于发送信号的socket对。// Returns -1 if we could not make the socket pair successfullyint zmq::signaler_t::make_fdpair(fd_t *r_, fd_t *w_){//通过eventfd实现线程间通信。#ifdefined ZMQ_HAVE_EVENTFDint flags =0;#ifdefined ZMQ_HAVE_EVENTFD_CLOEXEC// Setting this option result in sane behaviour when exec() functions// are used. Old sockets are closed and don't block TCP ports, avoid// leaks, etc.
flags |= EFD_CLOEXEC;#endif
fd_t fd =eventfd(0, flags);if(fd ==-1){errno_assert(errno == ENFILE || errno == EMFILE);*w_ =*r_ =-1;return-1;}else{*w_ =*r_ = fd;return0;}//如果不支持eventfd则采用如下方法。#else// All other implementations support socketpair()int sv [2];int type = SOCK_STREAM;// Setting this option result in sane behaviour when exec() functions// are used. Old sockets are closed and don't block TCP ports, avoid// leaks, etc.#ifdefined ZMQ_HAVE_SOCK_CLOEXEC
type |= SOCK_CLOEXEC;#endif//创建一个socket对。int rc =socketpair(AF_UNIX, type,0, sv);if(rc ==-1){errno_assert(errno == ENFILE || errno == EMFILE);*w_ =*r_ =-1;return-1;}else{// If there's no SOCK_CLOEXEC, let's try the second best option. Note that// race condition can cause socket not to be closed (if fork happens// between socket creation and this point).#if!defined ZMQ_HAVE_SOCK_CLOEXEC && defined FD_CLOEXEC
rc =fcntl(sv [0], F_SETFD, FD_CLOEXEC);//设置为非阻塞。errno_assert(rc !=-1);
rc =fcntl(sv [1], F_SETFD, FD_CLOEXEC);//设置为非阻塞。errno_assert(rc !=-1);#endif*w_ = sv [0];//0用于写线程。*r_ = sv [1];//1用于读线程。return0;}#endif