Class: IO::Event::Selector::EPoll

Inherits:
Object
  • Object
show all
Defined in:
ext/io/event/selector/epoll.c

Instance Method Summary collapse

Constructor Details

#initialize(loop) ⇒ Object



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'ext/io/event/selector/epoll.c', line 347

VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	IO_Event_Selector_initialize(&selector->backend, self, loop);
	int result = epoll_create1(EPOLL_CLOEXEC);
	
	if (result == -1) {
		rb_sys_fail("IO_Event_Selector_EPoll_initialize:epoll_create");
	} else {
		selector->descriptor = result;
		selector->owner = getpid();
		
		rb_update_max_fd(selector->descriptor);
	}
	
	IO_Event_Interrupt_open(&selector->interrupt);
	IO_Event_Interrupt_add(&selector->interrupt, selector);
	
	return self;
}

Instance Method Details

#closeObject



385
386
387
388
389
390
391
392
# File 'ext/io/event/selector/epoll.c', line 385

VALUE IO_Event_Selector_EPoll_close(VALUE self) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	close_internal(selector);
	
	return Qnil;
}

#closed?Boolean

Returns:

  • (Boolean)


394
395
396
397
398
399
# File 'ext/io/event/selector/epoll.c', line 394

VALUE IO_Event_Selector_EPoll_closed_p(VALUE self) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	return selector->descriptor < 0 || selector->owner != getpid() ? Qtrue : Qfalse;
}

#idle_durationObject



376
377
378
379
380
381
382
383
# File 'ext/io/event/selector/epoll.c', line 376

VALUE IO_Event_Selector_EPoll_idle_duration(VALUE self) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	double duration = selector->idle_duration.tv_sec + (selector->idle_duration.tv_nsec / 1000000000.0);
	
	return DBL2NUM(duration);
}

#io_read(*args) ⇒ Object



692
693
694
695
696
697
698
699
700
701
702
703
# File 'ext/io/event/selector/epoll.c', line 692

VALUE IO_Event_Selector_EPoll_io_read_compatible(int argc, VALUE *argv, VALUE self)
{
	rb_check_arity(argc, 4, 5);
	
	VALUE _offset = SIZET2NUM(0);
	
	if (argc == 5) {
		_offset = argv[4];
	}
	
	return IO_Event_Selector_EPoll_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
}

#io_wait(fiber, io, events) ⇒ Object



566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'ext/io/event/selector/epoll.c', line 566

VALUE IO_Event_Selector_EPoll_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	int descriptor = IO_Event_Selector_io_descriptor(io); 
	
	struct IO_Event_Selector_EPoll_Waiting waiting = {
		.list = {.type = &IO_Event_Selector_EPoll_io_wait_list_type},
		.fiber = fiber,
		.events = RB_NUM2INT(events),
	};
	
	RB_OBJ_WRITTEN(self, Qundef, fiber);
	
	int result = IO_Event_Selector_EPoll_Waiting_register(selector, io, descriptor, &waiting);
	
	if (result == -1) {
		if (errno == EPERM) {
			IO_Event_Selector_ready_push(&selector->backend, fiber);
			IO_Event_Selector_yield(&selector->backend);
			return events;
		}
		
		rb_sys_fail("IO_Event_Selector_EPoll_io_wait:IO_Event_Selector_EPoll_Waiting_register");
	}
	
	struct io_wait_arguments io_wait_arguments = {
		.selector = selector,
		.waiting = &waiting,
	};
	
	return rb_ensure(io_wait_transfer, (VALUE)&io_wait_arguments, io_wait_ensure, (VALUE)&io_wait_arguments);
}

#io_write(*args) ⇒ Object



799
800
801
802
803
804
805
806
807
808
809
810
# File 'ext/io/event/selector/epoll.c', line 799

VALUE IO_Event_Selector_EPoll_io_write_compatible(int argc, VALUE *argv, VALUE self)
{
	rb_check_arity(argc, 4, 5);
	
	VALUE _offset = SIZET2NUM(0);
	
	if (argc == 5) {
		_offset = argv[4];
	}
	
	return IO_Event_Selector_EPoll_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
}

#loopObject



369
370
371
372
373
374
# File 'ext/io/event/selector/epoll.c', line 369

VALUE IO_Event_Selector_EPoll_loop(VALUE self) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	return selector->backend.loop;
}

#process_wait(fiber, _pid, _flags) ⇒ Object

rb_define_method(IO_Event_Selector_EPoll, "io_write", IO_Event_Selector_EPoll_io_write, 5);



484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# File 'ext/io/event/selector/epoll.c', line 484

VALUE IO_Event_Selector_EPoll_process_wait(VALUE self, VALUE fiber, VALUE _pid, VALUE _flags) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	pid_t pid = NUM2PIDT(_pid);
	int flags = NUM2INT(_flags);
	
	// `pidfd_open` can only refer to a specific process, so waiting for any child or a process group (pid <= 0) is delegated to the threaded fallback:
	if (pid <= 0) {
		return IO_Event_Selector_process_wait(pid, flags);
	}
	
	int descriptor = pidfd_open(pid, 0);
	
	if (descriptor == -1) {
		rb_sys_fail("IO_Event_Selector_EPoll_process_wait:pidfd_open");
	}
	
	rb_update_max_fd(descriptor);
	
	// `pidfd_open` (above) may be edge triggered, so we need to check if the process is already exited, and if so, return immediately, otherwise we will block indefinitely.
	VALUE status = IO_Event_Selector_process_status_reap(pid, flags);
	if (status != Qnil) {
		close(descriptor);
		return status;
	}
	
	struct IO_Event_Selector_EPoll_Waiting waiting = {
		.list = {.type = &IO_Event_Selector_EPoll_process_wait_list_type},
		.fiber = fiber,
		.events = IO_EVENT_READABLE,
	};
	
	RB_OBJ_WRITTEN(self, Qundef, fiber);
	
	int result = IO_Event_Selector_EPoll_Waiting_register(selector, _pid, descriptor, &waiting);
	
	if (result == -1) {
		close(descriptor);
		rb_sys_fail("IO_Event_Selector_EPoll_process_wait:IO_Event_Selector_EPoll_Waiting_register");
	}
	
	struct process_wait_arguments process_wait_arguments = {
		.selector = selector,
		.pid = pid,
		.flags = flags,
		.descriptor = descriptor,
		.waiting = &waiting,
	};
	
	return rb_ensure(process_wait_transfer, (VALUE)&process_wait_arguments, process_wait_ensure, (VALUE)&process_wait_arguments);
}

#push(fiber) ⇒ Object



425
426
427
428
429
430
431
432
433
# File 'ext/io/event/selector/epoll.c', line 425

VALUE IO_Event_Selector_EPoll_push(VALUE self, VALUE fiber)
{
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	IO_Event_Selector_ready_push(&selector->backend, fiber);
	
	return Qnil;
}

#raise(*args) ⇒ Object



435
436
437
438
439
440
441
# File 'ext/io/event/selector/epoll.c', line 435

VALUE IO_Event_Selector_EPoll_raise(int argc, VALUE *argv, VALUE self)
{
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	return IO_Event_Selector_raise(&selector->backend, argc, argv);
}

#ready?Boolean

Returns:

  • (Boolean)


443
444
445
446
447
448
# File 'ext/io/event/selector/epoll.c', line 443

VALUE IO_Event_Selector_EPoll_ready_p(VALUE self) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	return selector->backend.ready ? Qtrue : Qfalse;
}

#resume(*args) ⇒ Object



409
410
411
412
413
414
415
# File 'ext/io/event/selector/epoll.c', line 409

VALUE IO_Event_Selector_EPoll_resume(int argc, VALUE *argv, VALUE self)
{
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	return IO_Event_Selector_resume(&selector->backend, argc, argv);
}

#select(duration) ⇒ Object

TODO This function is not re-entrant and we should document and assert as such.



1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
# File 'ext/io/event/selector/epoll.c', line 1034

VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	selector->idle_duration.tv_sec = 0;
	selector->idle_duration.tv_nsec = 0;
	
	int ready = IO_Event_Selector_ready_flush(&selector->backend);
	
	struct select_arguments arguments = {
		.selector = selector,
		.count = EPOLL_MAX_EVENTS,
		.result = 0,
		.storage = {
			.tv_sec = 0,
			.tv_nsec = 0
		},
		.saved = {},
	};

	arguments.timeout = &arguments.storage;

	// Process any currently pending events:
	int result = select_internal_with_gvl(&arguments);
	
	// If we:
	// 1. Didn't process any ready fibers, and
	// 2. Didn't process any events from non-blocking select (above), and
	// 3. There are no items in the ready list,
	// then we can perform a blocking select.
	if (!ready && !result && !selector->backend.ready) {
		arguments.timeout = make_timeout(duration, &arguments.storage);
		
		if (select_blocking_allowed(arguments.timeout)) {
			struct timespec start_time;
			IO_Event_Time_current(&start_time);
			
			// Wait for events to occur:
			result = select_internal_without_gvl(&arguments);
			
			struct timespec end_time;
			IO_Event_Time_current(&end_time);
			IO_Event_Time_elapsed(&start_time, &end_time, &selector->idle_duration);
		}
	}
	
	if (result) {
		return rb_ensure(select_handle_events, (VALUE)&arguments, select_handle_events_ensure, (VALUE)&arguments);
	} else {
		return RB_INT2NUM(0);
	}
}

#transferObject



401
402
403
404
405
406
407
# File 'ext/io/event/selector/epoll.c', line 401

VALUE IO_Event_Selector_EPoll_transfer(VALUE self)
{
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	return IO_Event_Selector_loop_yield(&selector->backend);
}

#wakeupObject



1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
# File 'ext/io/event/selector/epoll.c', line 1087

VALUE IO_Event_Selector_EPoll_wakeup(VALUE self) {
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	// If we are blocking, we can schedule a nop event to wake up the selector:
	if (selector->blocked) {
		IO_Event_Interrupt_signal(&selector->interrupt);
		
		return Qtrue;
	}
	
	return Qfalse;
}

#yieldObject



417
418
419
420
421
422
423
# File 'ext/io/event/selector/epoll.c', line 417

VALUE IO_Event_Selector_EPoll_yield(VALUE self)
{
	struct IO_Event_Selector_EPoll *selector = NULL;
	TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, selector);
	
	return IO_Event_Selector_yield(&selector->backend);
}