Operating Systems CMPSCI 377 Spring 2017 Mark Corner University of Massachusetts Amherst
Are threads it? Threads are not the only way to achieve concurrency Recall that our primary goal is to overlap I/O and CPU Our other goal is to user multiple CPUs (let s put that aside for a moment).
Threads review Recall why we need threads Our I/O calls block until done So while this thread waits, we need another thread to do useful work (or wait for other slow/ I/O) void function(){ do some slow I/O return;
Event-Based Concurrency If you program in Javascript (and some other languages), you will notice there are no threads! Then how do we overlap CPU and I/O? We do CPU work, then we fire off I/O, continue working, then a function we designate gets called when the I/O is done But before we talk about Javascript, lets do C++
Event Loops The heart of an event-based concurrency system is an event loop We wait for one of many things to happen, then we do something based on what happened while (1) { events = getevents(); for (e in events) processevent(e);
select() Systems (such as libc) provide a way to wait for one of many things to happen via select() We give select a set of file descriptors and it returns when the file descriptors have data to read, can accept a write, or has an error int select( int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout );
Clicker Question #1 Is select a blocking or non-blocking call? (A) It is a blocking call (B) It is a non-blocking call (C) Neither (D) Both
Answer on Next Slide
Simple Example Let s start with a simple example: waiting for data arriving on a set of file descriptors (like sockets) These are external events and happen at unpredictable times
What about locks? There aren t any. Really? Yes.
Single Threaded This event-based systems is single threaded. There is no interleaving of computation So no need for locks
Blocking calls When an event occurs the event loop will call an event handler for that kind of event What if the event handler wants to do I/O? It absolutely cannot make any blocking calls What happens if it does? The world stops
Asynchronous I/O Issue I/O calls that return immediately They don t have the results immediately, but the program can continue When the results are ready the event handler finds the right function to handle the results function get_image(){ aio_read(params ); function process_image(data){ do something with image function event_loop(){ while(1){ select(..); if event == image done process_image(data);
Spaghetti Quickly this leads to spaghetti code. It is really hard to tell where control goes after reading the image Enter: continuations Continuations are a general term for defining where to pass control when the I/O is done. Where does it continue
Callbacks function get_image(){ aio_read(params, process_image); function process_image(data){ do something with image
void setup_io(... ) { int fd; struct aiocb my_aiocb;... /* Set up the AIO request */ bzero( (char *)&my_aiocb, sizeof(struct aiocb) ); my_aiocb.aio_fildes = fd; my_aiocb.aio_buf = malloc(buf_size+1); my_aiocb.aio_nbytes = BUF_SIZE; my_aiocb.aio_offset = next_offset; /* Link the AIO request with a thread callback */ my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD; my_aiocb.aio_sigevent.notify_function = aio_completion_handler; my_aiocb.aio_sigevent.notify_attributes = NULL; my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;... ret = aio_read( &my_aiocb ); https://www.ibm.com/developerworks/linux/library/l-async/
Callback void aio_completion_handler( sigval_t sigval ) { struct aiocb *req; req = (struct aiocb *)sigval.sival_ptr; /* Did the request complete? */ if (aio_error( req ) == 0) { /* Request completed successfully, get the return status */ ret = aio_return( req ); return; https://www.ibm.com/developerworks/linux/library/l-async/
Javascript AIO in C++ is pretty hairy (isn t everything in C++?) But in Javascript it is how you *always do it*
Javascript Callbacks fs = require('fs') function callback(err,data) { if (err) { return console.log(err); console.log(data); fs.readfile('/etc/hosts', 'utf8', callback);
Clicker Question #2 What order will the files be printed out? (A) hosts then networks (B) networks then hosts (C) don t know fs = require('fs') function callback(err,data) { if (err) { return console.log(err); console.log(data); fs.readfile('/etc/hosts', 'utf8', callback); fs.readfile('/etc/networks', 'utf8', callback);
Answer on Next Slide