| /* Copyright (c) 2015 Google Inc | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * #eventfd test, using the glibc interface mostly. */ | 
 |  | 
 | #include <stdlib.h> | 
 | #include <stdio.h> | 
 | #include <parlib/parlib.h> | 
 | #include <unistd.h> | 
 | #include <pthread.h> | 
 |  | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #include <fcntl.h> | 
 | #include <sys/eventfd.h> | 
 | #include <sys/epoll.h> | 
 |  | 
 |  | 
 | #define handle_error(msg) \ | 
 |         do { perror(msg); exit(-1); } while (0) | 
 |  | 
 | static void epoll_on_efd(int efd) | 
 | { | 
 | 	#define EP_SET_SZ 10	/* this is actually the ID of the largest FD */ | 
 | 	int epfd = epoll_create(EP_SET_SZ); | 
 | 	struct epoll_event ep_ev; | 
 | 	struct epoll_event results[EP_SET_SZ]; | 
 |  | 
 | 	if (epfd < 0) | 
 | 		handle_error("epoll_create"); | 
 | 	ep_ev.events = EPOLLIN | EPOLLET; | 
 | 	ep_ev.data.fd = efd; | 
 | 	if (epoll_ctl(epfd, EPOLL_CTL_ADD, efd, &ep_ev)) | 
 | 		handle_error("epoll_ctl_add eventfd"); | 
 | 	if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) | 
 | 		handle_error("epoll_wait"); | 
 | 	close(epfd); | 
 | } | 
 |  | 
 | static pthread_attr_t pth_attrs; | 
 | static bool upped; | 
 |  | 
 | static void *upper_thread(void *arg) | 
 | { | 
 | 	int efd = (int)(long)arg; | 
 | 	uthread_sleep(1); | 
 | 	if (eventfd_write(efd, 1)) | 
 | 		handle_error("upper write"); | 
 | 	upped = TRUE; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int main(int argc, char **argv) | 
 | { | 
 | 	int ret; | 
 | 	eventfd_t efd_val = 0; | 
 | 	int efd; | 
 | 	pthread_t child; | 
 |  | 
 | 	parlib_wants_to_be_mcp = FALSE; /* Make us an SCP with a 2LS */ | 
 | 	pthread_attr_init(&pth_attrs); | 
 | 	if (pthread_attr_setdetachstate(&pth_attrs, PTHREAD_CREATE_DETACHED)) | 
 | 		handle_error("pth attrs"); | 
 |  | 
 | 	/* Semaphore counter, nonblocking */ | 
 | 	efd = eventfd(2, EFD_SEMAPHORE | EFD_NONBLOCK); | 
 | 	if (efd < 0) | 
 | 		handle_error("open sem"); | 
 | 	if (eventfd_read(efd, &efd_val)) | 
 | 		handle_error("first read"); | 
 | 	assert(efd_val == 1); | 
 | 	if (eventfd_read(efd, &efd_val)) | 
 | 		handle_error("second read"); | 
 | 	assert(efd_val == 1);	/* always get 1 back from the SEM */ | 
 | 	ret = eventfd_read(efd, &efd_val); | 
 | 	if ((ret != -1) && (errno != EAGAIN)) | 
 | 		handle_error("third read should be EAGAIN"); | 
 |  | 
 | 	if (pthread_create(&child, &pth_attrs, &upper_thread, (void*)(long)efd)) | 
 | 		handle_error("pth_create failed"); | 
 | 	epoll_on_efd(efd); | 
 | 	if (eventfd_read(efd, &efd_val)) | 
 | 		handle_error("final read"); | 
 | 	assert(efd_val == 1); | 
 | 	close(efd); | 
 |  | 
 | 	/* Regular counter */ | 
 | 	efd = eventfd(2, 0); | 
 | 	if (efd < 0) | 
 | 		handle_error("open nonsem"); | 
 | 	if (eventfd_read(efd, &efd_val)) | 
 | 		handle_error("first read nonsem"); | 
 | 	assert(efd_val == 2); | 
 |  | 
 | 	/* Will try to block in the kernel.  Using 'upped' to catch any quick | 
 | 	 * returns.  It's not full-proof, but it can catch an O_NONBLOCK */ | 
 | 	if (pthread_create(&child, &pth_attrs, &upper_thread, (void*)(long)efd)) | 
 | 		handle_error("pth_create failed"); | 
 | 	upped = FALSE; | 
 | 	if (eventfd_read(efd, &efd_val)) | 
 | 		handle_error("blocking read nonsem"); | 
 | 	cmb(); | 
 | 	assert(upped && efd_val == 1); | 
 |  | 
 | 	/* Should still be 0.  Add 1 and then extract to see if it was. */ | 
 | 	if (eventfd_write(efd, 1)) | 
 | 		handle_error("write nonsem +1"); | 
 | 	if (eventfd_read(efd, &efd_val)) | 
 | 		handle_error("final read nonsem"); | 
 | 	/* 1 means it was 0 before we added 1.  it's 0 again, since we read. */ | 
 | 	assert(efd_val == 1); | 
 | 	/* Test the max_val + 1 write */ | 
 | 	ret = eventfd_write(efd, -1); | 
 | 	if ((ret != -1) && (errno != EINVAL)) | 
 | 		handle_error("write nonsem should have failed"); | 
 | 	if (eventfd_write(efd, 0xfffffffffffffffe)) | 
 | 		handle_error("largest legal write"); | 
 | 	close(efd); | 
 |  | 
 | 	return 0; | 
 | } |