| // Copyright 2013 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | // +build solaris | 
 |  | 
 | #include "config.h" | 
 |  | 
 | #include <errno.h> | 
 | #include <sys/times.h> | 
 | #include <sys/types.h> | 
 | #include <unistd.h> | 
 | #include <fcntl.h> | 
 |  | 
 | #ifdef HAVE_SYS_SELECT_H | 
 | #include <sys/select.h> | 
 | #endif | 
 |  | 
 | #include "runtime.h" | 
 | #include "malloc.h" | 
 |  | 
 | static Lock selectlock; | 
 | static int rdwake; | 
 | static int wrwake; | 
 | static fd_set fds; | 
 | static PollDesc **data; | 
 | static int allocated; | 
 |  | 
 | void | 
 | runtime_netpollinit(void) | 
 | { | 
 | 	int p[2]; | 
 | 	int fl; | 
 |  | 
 | 	FD_ZERO(&fds); | 
 | 	allocated = 128; | 
 | 	data = runtime_mallocgc(allocated * sizeof(PollDesc *), 0, | 
 | 				FlagNoScan|FlagNoProfiling|FlagNoInvokeGC); | 
 |  | 
 | 	if(pipe(p) < 0) | 
 | 		runtime_throw("netpollinit: failed to create pipe"); | 
 | 	rdwake = p[0]; | 
 | 	wrwake = p[1]; | 
 |  | 
 | 	fl = fcntl(rdwake, F_GETFL); | 
 | 	if(fl < 0) | 
 | 		runtime_throw("netpollinit: fcntl failed"); | 
 | 	fl |= O_NONBLOCK; | 
 | 	if(fcntl(rdwake, F_SETFL, fl)) | 
 | 		 runtime_throw("netpollinit: fcntl failed"); | 
 | 	fcntl(rdwake, F_SETFD, FD_CLOEXEC); | 
 |  | 
 | 	fl = fcntl(wrwake, F_GETFL); | 
 | 	if(fl < 0) | 
 | 		runtime_throw("netpollinit: fcntl failed"); | 
 | 	fl |= O_NONBLOCK; | 
 | 	if(fcntl(wrwake, F_SETFL, fl)) | 
 | 		 runtime_throw("netpollinit: fcntl failed"); | 
 | 	fcntl(wrwake, F_SETFD, FD_CLOEXEC); | 
 |  | 
 | 	FD_SET(rdwake, &fds); | 
 | } | 
 |  | 
 | int32 | 
 | runtime_netpollopen(uintptr fd, PollDesc *pd) | 
 | { | 
 | 	byte b; | 
 |  | 
 | 	runtime_lock(&selectlock); | 
 |  | 
 | 	if((int)fd >= allocated) { | 
 | 		int c; | 
 | 		PollDesc **n; | 
 |  | 
 | 		c = allocated; | 
 |  | 
 | 		runtime_unlock(&selectlock); | 
 |  | 
 | 		while((int)fd >= c) | 
 | 			c *= 2; | 
 | 		n = runtime_mallocgc(c * sizeof(PollDesc *), 0, | 
 | 				     FlagNoScan|FlagNoProfiling|FlagNoInvokeGC); | 
 |  | 
 | 		runtime_lock(&selectlock); | 
 |  | 
 | 		if(c > allocated) { | 
 | 			__builtin_memcpy(n, data, allocated * sizeof(PollDesc *)); | 
 | 			allocated = c; | 
 | 			data = n; | 
 | 		} | 
 | 	} | 
 | 	FD_SET(fd, &fds); | 
 | 	data[fd] = pd; | 
 |  | 
 | 	runtime_unlock(&selectlock); | 
 |  | 
 | 	b = 0; | 
 | 	write(wrwake, &b, sizeof b); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int32 | 
 | runtime_netpollclose(uintptr fd) | 
 | { | 
 | 	byte b; | 
 |  | 
 | 	runtime_lock(&selectlock); | 
 |  | 
 | 	FD_CLR(fd, &fds); | 
 | 	data[fd] = nil; | 
 |  | 
 | 	runtime_unlock(&selectlock); | 
 |  | 
 | 	b = 0; | 
 | 	write(wrwake, &b, sizeof b); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Used to avoid using too much stack memory.  */ | 
 | static bool inuse; | 
 | static fd_set grfds, gwfds, gefds, gtfds; | 
 |  | 
 | G* | 
 | runtime_netpoll(bool block) | 
 | { | 
 | 	fd_set *prfds, *pwfds, *pefds, *ptfds; | 
 | 	bool allocatedfds; | 
 | 	struct timeval timeout; | 
 | 	struct timeval *pt; | 
 | 	int max, c, i; | 
 | 	G *gp; | 
 | 	int32 mode; | 
 | 	byte b; | 
 | 	struct stat st; | 
 |  | 
 | 	allocatedfds = false; | 
 |  | 
 |  retry: | 
 | 	runtime_lock(&selectlock); | 
 |  | 
 | 	max = allocated; | 
 |  | 
 | 	if(max == 0) { | 
 | 		runtime_unlock(&selectlock); | 
 | 		return nil; | 
 | 	} | 
 |  | 
 | 	if(inuse) { | 
 | 		if(!allocatedfds) { | 
 | 			prfds = runtime_SysAlloc(4 * sizeof fds, &mstats.other_sys); | 
 | 			pwfds = prfds + 1; | 
 | 			pefds = pwfds + 1; | 
 | 			ptfds = pefds + 1; | 
 | 			allocatedfds = true; | 
 | 		} | 
 | 	} else { | 
 | 		prfds = &grfds; | 
 | 		pwfds = &gwfds; | 
 | 		pefds = &gefds; | 
 | 		ptfds = >fds; | 
 | 		inuse = true; | 
 | 		allocatedfds = false; | 
 | 	} | 
 |  | 
 | 	__builtin_memcpy(prfds, &fds, sizeof fds); | 
 |  | 
 | 	runtime_unlock(&selectlock); | 
 |  | 
 | 	__builtin_memcpy(pwfds, prfds, sizeof fds); | 
 | 	FD_CLR(rdwake, pwfds); | 
 | 	__builtin_memcpy(pefds, pwfds, sizeof fds); | 
 |  | 
 | 	__builtin_memcpy(ptfds, pwfds, sizeof fds); | 
 |  | 
 | 	__builtin_memset(&timeout, 0, sizeof timeout); | 
 | 	pt = &timeout; | 
 | 	if(block) | 
 | 		pt = nil; | 
 |  | 
 | 	c = select(max, prfds, pwfds, pefds, pt); | 
 | 	if(c < 0) { | 
 | 		if(errno == EBADF) { | 
 | 			// Some file descriptor has been closed. | 
 | 			// Check each one, and treat each closed | 
 | 			// descriptor as ready for read/write. | 
 | 			c = 0; | 
 | 			FD_ZERO(prfds); | 
 | 			FD_ZERO(pwfds); | 
 | 			FD_ZERO(pefds); | 
 | 			for(i = 0; i < max; i++) { | 
 | 				if(FD_ISSET(i, ptfds) | 
 | 				   && fstat(i, &st) < 0 | 
 | 				   && errno == EBADF) { | 
 | 					FD_SET(i, prfds); | 
 | 					FD_SET(i, pwfds); | 
 | 					c += 2; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		else { | 
 | 			if(errno != EINTR) | 
 | 				runtime_printf("runtime: select failed with %d\n", errno); | 
 | 			goto retry; | 
 | 		} | 
 | 	} | 
 | 	gp = nil; | 
 | 	for(i = 0; i < max && c > 0; i++) { | 
 | 		mode = 0; | 
 | 		if(FD_ISSET(i, prfds)) { | 
 | 			mode += 'r'; | 
 | 			--c; | 
 | 		} | 
 | 		if(FD_ISSET(i, pwfds)) { | 
 | 			mode += 'w'; | 
 | 			--c; | 
 | 		} | 
 | 		if(FD_ISSET(i, pefds)) { | 
 | 			mode = 'r' + 'w'; | 
 | 			--c; | 
 | 		} | 
 | 		if(i == rdwake && mode != 0) { | 
 | 			while(read(rdwake, &b, sizeof b) > 0) | 
 | 				; | 
 | 			continue; | 
 | 		} | 
 | 		if(mode) { | 
 | 			PollDesc *pd; | 
 |  | 
 | 			runtime_lock(&selectlock); | 
 | 			pd = data[i]; | 
 | 			runtime_unlock(&selectlock); | 
 | 			if(pd != nil) | 
 | 				runtime_netpollready(&gp, pd, mode); | 
 | 		} | 
 | 	} | 
 | 	if(block && gp == nil) | 
 | 		goto retry; | 
 |  | 
 | 	if(allocatedfds) { | 
 | 		runtime_SysFree(prfds, 4 * sizeof fds, &mstats.other_sys); | 
 | 	} else { | 
 | 		runtime_lock(&selectlock); | 
 | 		inuse = false; | 
 | 		runtime_unlock(&selectlock); | 
 | 	} | 
 |  | 
 | 	return gp; | 
 | } | 
 |  | 
 | void | 
 | runtime_netpoll_scan(struct Workbuf** wbufp, void (*enqueue1)(struct Workbuf**, Obj)) | 
 | { | 
 | 	enqueue1(wbufp, (Obj){(byte*)&data, sizeof data, 0}); | 
 | } |