blob: 4e2b09ae1d1cd1f9850504b93bbab63aac7b2ef5 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <cc++/config.h>
40#include <cc++/thread.h>
41#include <cc++/address.h>
42#include <cc++/socket.h>
43#include <cc++/export.h>
44#include <cc++/socketport.h>
45#include "private.h"
46#ifndef WIN32
47#include <cerrno>
48#define socket_errno errno
49#else
50#define socket_errno WSAGetLastError()
51#endif
52
53#ifndef INADDR_LOOPBACK
54#define INADDR_LOOPBACK (unsigned long)0x7f000001
55#endif
56
57#ifdef CCXX_NAMESPACES
58namespace ost {
59#endif
60
61SocketPort::SocketPort(SocketService *svc, TCPSocket &tcp) :
62Socket(accept(tcp.getSocket(), NULL, NULL))
63{
64 detect_pending = true;
65 detect_output = false;
66 detect_disconnect = true;
67
68#ifdef WIN32
69 // FIXME: error handling
70 event = CreateEvent(NULL,TRUE,FALSE,NULL);
71#endif
72 next = prev = NULL;
73 service = NULL;
74
75 // FIXME: use macro here and in other files...
76#ifndef WIN32
77 if(so > -1)
78#else
79 if(so != INVALID_SOCKET)
80#endif
81 {
82 setError(false);
83 if( svc )
84 svc->attach(this);
85 }
86}
87
88#ifdef CCXX_IPV6
89SocketPort::SocketPort(SocketService *svc, TCPV6Socket &tcp) :
90Socket(accept(tcp.getSocket(), NULL, NULL))
91{
92 detect_pending = true;
93 detect_output = false;
94 detect_disconnect = true;
95
96#ifdef WIN32
97 // FIXME: error handling
98 event = CreateEvent(NULL,TRUE,FALSE,NULL);
99#endif
100 next = prev = NULL;
101 service = NULL;
102
103 // FIXME: use macro here and in other files...
104#ifndef WIN32
105 if(so > -1)
106#else
107 if(so != INVALID_SOCKET)
108#endif
109 {
110 setError(false);
111 if( svc )
112 svc->attach(this);
113 }
114}
115#endif
116
117SocketPort::SocketPort(SocketService *svc, const IPV4Address &ia, tpport_t port) :
118Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
119{
120#ifdef WIN32
121 // FIXME: error handling
122 event = CreateEvent(NULL,TRUE,FALSE,NULL);
123#endif
124 struct sockaddr_in addr;
125
126 memset(&addr, 0, sizeof(addr));
127 next = prev = NULL;
128 service = NULL;
129 addr.sin_family = AF_INET;
130 addr.sin_addr = getaddress(ia);
131 addr.sin_port = htons(port);
132 detect_pending = true;
133 detect_output = false;
134 detect_disconnect = true;
135
136 if(bind(so, (struct sockaddr *)&addr, (socklen_t)sizeof(addr))) {
137 endSocket();
138 error(errBindingFailed,(char *)"Could not bind socket",socket_errno);
139 return;
140 }
141 state = BOUND;
142 setError(false);
143
144 if(svc)
145 svc->attach(this);
146}
147
148#ifdef CCXX_IPV6
149SocketPort::SocketPort(SocketService *svc, const IPV6Address &ia, tpport_t port) :
150Socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
151{
152#ifdef WIN32
153 // FIXME: error handling
154 event = CreateEvent(NULL,TRUE,FALSE,NULL);
155#endif
156 struct sockaddr_in6 addr;
157
158 memset(&addr, 0, sizeof(addr));
159 next = prev = NULL;
160 service = NULL;
161 addr.sin6_family = AF_INET6;
162 addr.sin6_addr = getaddress(ia);
163 addr.sin6_port = htons(port);
164 detect_pending = true;
165 detect_output = false;
166 detect_disconnect = true;
167
168 if(bind(so, (struct sockaddr *)&addr, (socklen_t)sizeof(addr))) {
169 endSocket();
170 error(errBindingFailed,(char *)"Could not bind socket",socket_errno);
171 return;
172 }
173 state = BOUND;
174 setError(false);
175
176 if(svc)
177 svc->attach(this);
178}
179#endif
180
181SocketPort::SocketPort(SocketService *svc, const IPV4Host &ih, tpport_t port) :
182Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
183{
184#ifdef WIN32
185 // FIXME: error handling
186 event = CreateEvent(NULL,TRUE,FALSE,NULL);
187#endif
188 struct sockaddr_in addr;
189
190 memset(&addr, 0, sizeof(addr));
191 next = prev = NULL;
192 service = NULL;
193 addr.sin_family = AF_INET;
194 addr.sin_addr = getaddress(ih);
195 addr.sin_port = htons(port);
196 detect_pending = true;
197 detect_disconnect = true;
198
199#ifndef WIN32
200 long opts = fcntl(so, F_GETFL);
201 fcntl(so, F_SETFL, opts | O_NDELAY);
202#else
203 u_long opts = 1;
204 ioctlsocket(so,FIONBIO,&opts);
205#endif
206
207 int rtn = ::connect(so, (struct sockaddr *)&addr, (socklen_t)sizeof(addr));
208
209 if(!rtn) {
210 state = CONNECTED;
211 }
212 else {
213#ifndef WIN32
214 if(errno == EINPROGRESS)
215#else
216 if(WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK)
217#endif
218 {
219 state = CONNECTING;
220 }
221 else {
222 endSocket();
223 connectError();
224 return;
225 }
226 }
227
228#ifndef WIN32
229 fcntl(so, F_SETFL, opts);
230#else
231 opts = 0;
232 ioctlsocket(so,FIONBIO,&opts);
233#endif
234
235 setError(false);
236 detect_output = (state == CONNECTING);
237
238 if(svc)
239 svc->attach(this);
240
241// if(state == CONNECTING)
242// setDetectOutput(true);
243}
244
245#ifdef CCXX_IPV6
246SocketPort::SocketPort(SocketService *svc, const IPV6Host &ih, tpport_t port) :
247Socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)
248{
249#ifdef WIN32
250 // FIXME: error handling
251 event = CreateEvent(NULL,TRUE,FALSE,NULL);
252#endif
253 struct sockaddr_in6 addr;
254
255 memset(&addr, 0, sizeof(addr));
256 next = prev = NULL;
257 service = NULL;
258 addr.sin6_family = AF_INET6;
259 addr.sin6_addr = getaddress(ih);
260 addr.sin6_port = htons(port);
261 detect_pending = true;
262 detect_disconnect = true;
263
264#ifndef WIN32
265 long opts = fcntl(so, F_GETFL);
266 fcntl(so, F_SETFL, opts | O_NDELAY);
267#else
268 u_long opts = 1;
269 ioctlsocket(so,FIONBIO,&opts);
270#endif
271
272 int rtn = ::connect(so, (struct sockaddr *)&addr, (socklen_t)sizeof(addr));
273
274 if(!rtn) {
275 state = CONNECTED;
276 }
277 else {
278#ifndef WIN32
279 if(errno == EINPROGRESS)
280#else
281 if(WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK)
282#endif
283 {
284 state = CONNECTING;
285 }
286 else {
287 endSocket();
288 connectError();
289 return;
290 }
291 }
292
293#ifndef WIN32
294 fcntl(so, F_SETFL, opts);
295#else
296 opts = 0;
297 ioctlsocket(so,FIONBIO,&opts);
298#endif
299
300 setError(false);
301 detect_output = (state == CONNECTING);
302
303 if(svc)
304 svc->attach(this);
305
306// if(state == CONNECTING)
307// setDetectOutput(true);
308}
309#endif
310
311SocketPort::~SocketPort()
312{
313#ifdef WIN32
314 CloseHandle(event);
315#endif
316 if(service) {
317 service->detach(this);
318 }
319 endSocket();
320}
321
322void SocketPort::expired(void)
323{}
324
325void SocketPort::pending(void)
326{}
327
328void SocketPort::output(void)
329{}
330
331void SocketPort::disconnect(void)
332{}
333
334void SocketPort::attach( SocketService* svc )
335{
336 if(service)
337 service->detach(this);
338 service = svc;
339 if(svc)
340 svc->attach(this);
341}
342
343Socket::Error SocketPort::connect(const IPV4Address &ia, tpport_t port)
344{
345 struct sockaddr_in addr;
346 Error rtn = errSuccess;
347
348 memset(&addr, 0, sizeof(addr));
349 addr.sin_family = AF_INET;
350 addr.sin_addr = getaddress(ia);
351 addr.sin_port = htons(port);
352
353#ifndef WIN32
354 long opts = fcntl(so, F_GETFL);
355 fcntl(so, F_SETFL, opts | O_NDELAY);
356#else
357 u_long opts = 1;
358 ioctlsocket(so,FIONBIO,&opts);
359#endif
360 // Win32 will crash if you try to connect to INADDR_ANY.
361 if ( INADDR_ANY == addr.sin_addr.s_addr )
362 addr.sin_addr.s_addr = INADDR_LOOPBACK;
363 if(::connect(so, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)))
364 rtn = connectError();
365#ifndef WIN32
366 fcntl(so, F_SETFL, opts);
367#else
368 opts = 0;
369 ioctlsocket(so,FIONBIO,&opts);
370#endif
371 return rtn;
372}
373
374#ifdef CCXX_IPV6
375Socket::Error SocketPort::connect(const IPV6Address &ia, tpport_t port)
376{
377 struct sockaddr_in6 addr;
378 Error rtn = errSuccess;
379
380 memset(&addr, 0, sizeof(addr));
381 addr.sin6_family = AF_INET6;
382 addr.sin6_addr = getaddress(ia);
383 addr.sin6_port = htons(port);
384
385#ifndef WIN32
386 long opts = fcntl(so, F_GETFL);
387 fcntl(so, F_SETFL, opts | O_NDELAY);
388#else
389 u_long opts = 1;
390 ioctlsocket(so,FIONBIO,&opts);
391#endif
392 // Win32 will crash if you try to connect to INADDR_ANY.
393 if(!memcmp(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)))
394 memcpy(&addr.sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback));
395 if(::connect(so, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)))
396 rtn = connectError();
397#ifndef WIN32
398 fcntl(so, F_SETFL, opts);
399#else
400 opts = 0;
401 ioctlsocket(so,FIONBIO,&opts);
402#endif
403 return rtn;
404}
405#endif
406
407void SocketPort::setTimer(timeout_t ptimer)
408{
409 TimerPort::setTimer(ptimer);
410 if( service )
411 service->update();
412}
413
414void SocketPort::incTimer(timeout_t ptimer)
415{
416 TimerPort::incTimer(ptimer);
417 if( service )
418 service->update();
419}
420
421void SocketPort::setDetectPending( bool val )
422{
423 if ( detect_pending != val ) {
424 detect_pending = val;
425#ifdef USE_POLL
426 if ( ufd ) {
427 if ( val ) {
428 ufd->events |= POLLIN;
429 } else {
430 ufd->events &= ~POLLIN;
431 }
432 }
433#endif
434 if( service )
435 service->update();
436 }
437}
438
439void SocketPort::setDetectOutput( bool val )
440{
441 if ( detect_output != val ) {
442 detect_output = val;
443#ifdef USE_POLL
444 if ( ufd ) {
445 if ( val ) {
446 ufd->events |= POLLOUT;
447 } else {
448 ufd->events &= ~POLLOUT;
449 }
450 }
451#endif
452 if( service )
453 service->update();
454 }
455}
456
457#ifdef WIN32
458class SocketService::Sync
459{
460public:
461 /* FIXME: error handling */
462 Sync() :
463 sync(CreateEvent(NULL,TRUE,FALSE,NULL)),
464 semWrite(CreateSemaphore(NULL,1,1,NULL)),
465 flag(-1)
466 {}
467
468 ~Sync() {
469 CloseHandle(sync);
470 CloseHandle(semWrite);
471 }
472
473 HANDLE GetSync() const {
474 return sync;
475 }
476
477 void update(unsigned char flag) {
478 // FIXME: cancellation
479 WaitForSingleObject(semWrite,INFINITE);
480 this->flag = flag;
481 SetEvent(sync);
482 }
483
484 int getFlag() {
485 int res = flag;
486 flag = -1;
487 if (res > 0) {
488 ReleaseSemaphore(semWrite,1,NULL);
489 ResetEvent(sync);
490 }
491 return res;
492 }
493private:
494 HANDLE sync;
495 HANDLE semWrite;
496 int flag;
497};
498#endif
499
500SocketService::SocketService(int pri, size_t stack, const char *id) :
501Thread(pri, stack), Mutex(id)
502{
503 first = last = NULL;
504 count = 0;
505#ifndef WIN32
506 FD_ZERO(&connect);
507 long opt;
508 if(::pipe(iosync)) {
509#ifdef CCXX_EXCEPTIONS
510 switch(Thread::getException()) {
511 case throwObject:
512 throw(this);
513 return;
514#ifdef COMMON_STD_EXCEPTION
515 case throwException:
516 throw(ThrException("no service pipe"));
517 return;
518#endif
519 default:
520 return;
521 }
522#else
523 return;
524#endif
525 }
526 hiwater = iosync[0] + 1;
527
528#ifndef USE_POLL
529 FD_SET(iosync[0], &connect);
530#endif
531 opt = fcntl(iosync[0], F_GETFL);
532 fcntl(iosync[0], F_SETFL, opt | O_NDELAY);
533#else
534 sync = new Sync();
535#endif
536}
537
538SocketService::~SocketService()
539{
540 update(0);
541
542#ifdef WIN32
543 // FIXME: thread is finished ???
544 delete sync;
545#endif
546
547 terminate();
548
549 while(first)
550 delete first;
551}
552
553void SocketService::onUpdate(unsigned char buf)
554{}
555
556void SocketService::onEvent(void)
557{}
558
559void SocketService::onCallback(SocketPort *port)
560{}
561
562void SocketService::attach(SocketPort *port)
563{
564 enterMutex();
565#ifdef USE_POLL
566 port->ufd = 0;
567#endif
568 if(last)
569 last->next = port;
570
571 port->prev = last;
572 last = port;
573#ifndef WIN32
574#ifndef USE_POLL
575 FD_SET(port->so, &connect);
576#endif
577 if(port->so >= hiwater)
578 hiwater = port->so + 1;
579#endif
580 port->service = this;
581
582 ++count;
583 if(!first) first = port;
584
585 // start thread if necessary
586 if (count == 1) {
587 if (!isRunning()) {
588 leaveMutex();
589 start();
590 return;
591 }
592 }
593 leaveMutex();
594 update();
595}
596
597void SocketService::detach(SocketPort *port)
598{
599 enterMutex();
600#if !defined(USE_POLL) && !defined(WIN32)
601 FD_CLR(port->so, &connect);
602#endif
603 if(port->prev) {
604 port->prev->next = port->next;
605 } else {
606 first = port->next;
607 }
608
609 if(port->next) {
610 port->next->prev = port->prev;
611 } else {
612 last = port->prev;
613 }
614 port->service = NULL;
615
616 --count;
617 leaveMutex();
618 update();
619}
620
621void SocketService::update(unsigned char flag)
622{
623#ifndef WIN32
624 if(::write(iosync[1], (char *)&flag, 1) < 1) {
625
626#ifdef CCXX_EXCEPTIONS
627 switch(Thread::getException()) {
628 case throwObject:
629 throw(this);
630 return;
631#ifdef COMMON_STD_EXCEPTION
632 case throwException:
633 throw(ThrException("update failed"));
634 return;
635#endif
636 default:
637 return;
638 }
639#else
640 return;
641#endif
642 }
643
644#else
645 sync->update(flag);
646#endif
647}
648
649#define MUTEX_START { MutexLock _lock_(*this);
650#define MUTEX_END }
651
652void SocketService::run(void)
653{
654 timeout_t timer, expires;
655 SocketPort *port;
656 unsigned char buf;
657
658#ifndef WIN32
659#ifdef USE_POLL
660
661 Poller mfd;
662 pollfd * p_ufd;
663 int lastcount = 0;
664
665 // initialize ufd in all attached ports :
666 // probably don't need this but it can't hurt.
667 enterMutex();
668 port = first;
669 while(port) {
670 port->ufd = 0;
671 port = port->next;
672 }
673 leaveMutex();
674
675#else
676 struct timeval timeout, *tvp;
677 fd_set inp, out, err;
678 FD_ZERO(&inp);
679 FD_ZERO(&out);
680 FD_ZERO(&err);
681 int so;
682#endif
683#else // WIN32
684 int numHandle = 0;
685 HANDLE hv[MAXIMUM_WAIT_OBJECTS];
686#endif
687
688
689#ifdef WIN32
690 // FIXME: needed ?
691 ResetEvent(sync->GetSync());
692#endif
693
694 setCancel(cancelDeferred);
695 for(;;) {
696 timer = TIMEOUT_INF;
697#ifndef WIN32
698 while(1 == ::read(iosync[0], (char *)&buf, 1)) {
699#else
700 for(;;) {
701 int f = sync->getFlag();
702 if (f < 0)
703 break;
704
705 buf = f;
706#endif
707 if(buf) {
708 onUpdate(buf);
709 continue;
710 }
711
712 setCancel(cancelImmediate);
713 sleep(TIMEOUT_INF);
714 exit();
715 }
716
717#ifndef WIN32
718#ifdef USE_POLL
719
720 bool reallocate = false;
721
722 MUTEX_START
723 onEvent();
724 port = first;
725 while(port) {
726 onCallback(port);
727 if ( ( p_ufd = port->ufd ) ) {
728
729 if ( ( POLLHUP | POLLNVAL ) & p_ufd->revents ) {
730 // Avoid infinite loop from disconnected sockets
731 port->detect_disconnect = false;
732 p_ufd->events &= ~POLLHUP;
733
734 SocketPort* p = port;
735 port = port->next;
736 detach(p);
737 reallocate = true;
738 p->disconnect();
739 continue;
740 }
741
742 if ( ( POLLIN | POLLPRI ) & p_ufd->revents )
743 port->pending();
744
745 if ( POLLOUT & p_ufd->revents )
746 port->output();
747
748 } else {
749 reallocate = true;
750 }
751
752retry:
753 expires = port->getTimer();
754
755 if(expires > 0)
756 if(expires < timer)
757 timer = expires;
758
759 if(!expires) {
760 port->endTimer();
761 port->expired();
762 goto retry;
763 }
764
765 port = port->next;
766 }
767
768 //
769 // reallocate things if we saw a ServerPort without
770 // ufd set !
771 if ( reallocate || ( ( count + 1 ) != lastcount ) ) {
772 lastcount = count + 1;
773 p_ufd = mfd.getList( count + 1 );
774
775 // Set up iosync polling
776 p_ufd->fd = iosync[0];
777 p_ufd->events = POLLIN | POLLHUP;
778 p_ufd ++;
779
780 port = first;
781 while(port) {
782 p_ufd->fd = port->so;
783 p_ufd->events =
784 ( port->detect_disconnect ? POLLHUP : 0 )
785 | ( port->detect_output ? POLLOUT : 0 )
786 | ( port->detect_pending ? POLLIN : 0 )
787 ;
788 port->ufd = p_ufd;
789 p_ufd ++;
790 port = port->next;
791 }
792 }
793 MUTEX_END
794 poll( mfd.getList(), lastcount, timer );
795
796#else
797 MUTEX_START
798 onEvent();
799 port = first;
800 while(port) {
801 onCallback(port);
802 so = port->so;
803 if(FD_ISSET(so, &err)) {
804 port->detect_disconnect = false;
805
806 SocketPort* p = port;
807 port = port->next;
808 p->disconnect();
809 continue;
810 }
811
812 if(FD_ISSET(so, &inp))
813 port->pending();
814
815 if(FD_ISSET(so, &out))
816 port->output();
817
818retry:
819 expires = port->getTimer();
820 if(expires > 0)
821 if(expires < timer)
822 timer = expires;
823
824 // if we expire, get new scheduling now
825
826 if(!expires) {
827 port->endTimer();
828 port->expired();
829 goto retry;
830 }
831
832 port = port->next;
833 }
834
835 FD_ZERO(&inp);
836 FD_ZERO(&out);
837 FD_ZERO(&err);
838 FD_SET(iosync[0],&inp);
839 port = first;
840 while(port) {
841 so = port->so;
842 if(port->detect_pending)
843 FD_SET(so, &inp);
844
845 if(port->detect_output)
846 FD_SET(so, &out);
847
848 if(port->detect_disconnect)
849 FD_SET(so, &err);
850
851 port = port->next;
852 }
853
854 MUTEX_END
855 if(timer == TIMEOUT_INF)
856 tvp = NULL;
857 else {
858 tvp = &timeout;
859 timeout.tv_sec = timer / 1000;
860 timeout.tv_usec = (timer % 1000) * 1000;
861 }
862 select(hiwater, &inp, &out, &err, tvp);
863#endif
864#else // WIN32
865 MUTEX_START
866 onEvent();
867
868 hv[0] = sync->GetSync();
869 numHandle = 1;
870 port = first;
871 while(port) {
872 onCallback(port);
873
874 long events = 0;
875
876 if(port->detect_pending)
877 events |= FD_READ;
878
879 if(port->detect_output)
880 events |= FD_WRITE;
881
882 if(port->detect_disconnect)
883 events |= FD_CLOSE;
884
885 // !!! ignore some socket on overflow !!!
886 if (events && numHandle < MAXIMUM_WAIT_OBJECTS) {
887 WSAEventSelect(port->so,port->event,events);
888 hv[numHandle++] = port->event;
889 }
890
891retry:
892 expires = port->getTimer();
893 if(expires > 0)
894 if(expires < timer)
895 timer = expires;
896
897 // if we expire, get new scheduling now
898
899 if(!expires) {
900 port->endTimer();
901 port->expired();
902 goto retry;
903 }
904
905 port = port->next;
906 }
907
908 MUTEX_END
909
910 // FIXME: handle thread cancellation correctly
911 DWORD res = WaitForMultipleObjects(numHandle,hv,FALSE,timer);
912 switch (res) {
913 case WAIT_OBJECT_0:
914 break;
915 case WAIT_TIMEOUT:
916 break;
917 default:
918 // FIXME: handle failures (detach SocketPort)
919 if (res >= WAIT_OBJECT_0+1 && res <= WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS) {
920 int curr = res - (WAIT_OBJECT_0);
921 WSANETWORKEVENTS events;
922
923 // search port
924 MUTEX_START
925 port = first;
926 while(port) {
927 if (port->event == hv[curr])
928 break;
929 port = port->next;
930 }
931 MUTEX_END
932
933 // if port not found ignore
934 if (!port || port->event != hv[curr])
935 break;
936
937 WSAEnumNetworkEvents(port->so,port->event,&events);
938
939 if(events.lNetworkEvents & FD_CLOSE) {
940 port->detect_disconnect = false;
941 port->disconnect();
942 continue;
943 }
944
945 if(events.lNetworkEvents & FD_READ)
946 port->pending();
947
948 if(events.lNetworkEvents & FD_WRITE)
949 port->output();
950 }
951 }
952#endif
953 }
954}
955
956#ifdef CCXX_NAMESPACES
957}
958#endif
959
960/** EMACS **
961 * Local variables:
962 * mode: c++
963 * c-basic-offset: 4
964 * End:
965 */