aboutsummaryrefslogblamecommitdiff
path: root/src/lib/libkogata/mainloop.c
blob: 75fb2529a996fd88cdf346b0f3a7140552d62391 (plain) (tree)
1
2
3
4
5
6
7
8
9
                   

                   
                            




                                 


                                     













































                                                                                                




                                                         





                                   

                                     





                                                                                      

                                              






















                                                                                                
                                                            
                                                                                                        
                          



                                                                                           
                 
                                                          




                                                                                                                
                                                                  


                                                                                                  
                                                                                                                                               
                                                                                          
                                                                                                                      
                                                                      
                                                                            





                                                                                                           
                                                                                                              

                                                                                            
                                                                                                                    



                                                                                                       
                                                                                            



                                         
                                                









                                   
#include <stdlib.h>
#include <string.h>

#include <kogata/mainloop.h>

mainloop_fd_t *mainloop_fds = 0;
bool mainloop_fds_change = false;
bool mainloop_must_exit = false;

idle_callback_t mainloop_idle_cb = 0;
void* mainloop_idle_cb_data = 0;

void mainloop_add_fd(mainloop_fd_t* fd) {
	mainloop_fds_change = true;

	fd->next = mainloop_fds;
	mainloop_fds = fd;

	fd->rd_buf_filled = 0;
}

void mainloop_rm_fd(mainloop_fd_t* fd) {
	mainloop_fds_change = true;

	if (mainloop_fds == fd) {
		mainloop_fds = fd->next;
	} else {
		for (mainloop_fd_t *it = mainloop_fds; it->next != 0; it = it->next) {
			if (it->next == fd) {
				it->next = fd->next;
				break;
			}
		}
	}
}

void mainloop_expect(mainloop_fd_t *fd, void* buf, size_t size, buf_full_callback_t cb) {
	fd->rd_buf = buf;
	fd->rd_on_full = cb;
	fd->rd_buf_expect_size = size;
	fd->rd_buf_filled = 0;
}

bool mainloop_nonblocking_write(mainloop_fd_t *fd, void* buf, size_t size, bool must_free_buf) {
	for (int i = 0; i < MAINLOOP_MAX_WR_BUFS; i++) {
		if (fd->wr_bufs[i].buf == 0) {
			fd->wr_bufs[i].buf = buf;
			fd->wr_bufs[i].written = 0;
			fd->wr_bufs[i].size = size;
			fd->wr_bufs[i].must_free = must_free_buf;

			return true;
		}
	}

	return false;
}

void mainloop_when_idle(idle_callback_t cb, void* data) {
	mainloop_idle_cb = cb;
	mainloop_idle_cb_data = data;
}

void mainloop_run() {
	sel_fd_t *sel_arg = 0;
	int nfds = 0;

	mainloop_fds_change = true;
	mainloop_must_exit = false;

	bool after_idle_step = false;
	while(!mainloop_must_exit) {
		if (mainloop_fds_change) {
			nfds = 0;
			for (mainloop_fd_t *fd = mainloop_fds; fd != 0; fd = fd->next)
				nfds++;

			if (nfds == 0) return;

			if (sel_arg != 0) free(sel_arg);
			sel_arg = (sel_fd_t*)malloc(nfds * sizeof(sel_fd_t));
			if (sel_arg == 0) {
				dbg_printf("(mainloop) Out of memory.\n");
				return;
			}

			
			mainloop_fds_change = false;
		}

		{	// Setup flags we are waiting for
			int i = 0;
			for (mainloop_fd_t *fd = mainloop_fds; fd != 0; fd = fd->next) {
				sel_arg[i].fd = fd->fd;
				sel_arg[i].req_flags =
					(fd->rd_buf != 0 ? SEL_READ : 0)
					| (fd->wr_bufs[0].buf != 0 ? SEL_WRITE : 0) | SEL_ERROR;
				i++;
			}
		}

		//  ---- Do the select
		/*dbg_printf("(mainloop) begin select\n");*/
		bool ok = sc_select(sel_arg, nfds, (after_idle_step || mainloop_idle_cb == 0 ? -1 : 0));
		if (!ok) {
			// nothing happenned
			if (mainloop_idle_cb != 0) mainloop_idle_cb(mainloop_idle_cb_data);
			after_idle_step = true;
			continue;
		}
		/*dbg_printf("(mainloop) end select\n");*/
		
		{	// Parse result
			int i = 0;
			for (mainloop_fd_t *fd = mainloop_fds; fd != 0 && !mainloop_fds_change; fd = fd->next) {
				if (sel_arg[i].got_flags & SEL_ERROR) {
					ASSERT(fd->on_error != 0);
					fd->on_error(fd);
				} else if ((sel_arg[i].got_flags & SEL_READ) && fd->rd_buf != 0) {
					fd->rd_buf_filled +=
						sc_read(fd->fd, 0, fd->rd_buf_expect_size - fd->rd_buf_filled, fd->rd_buf + fd->rd_buf_filled);
					if (fd->rd_buf_filled == fd->rd_buf_expect_size) {
						/*dbg_printf("(mainloop) finish read %d\n", fd->rd_buf_expect_size);*/
						fd->rd_buf_filled = 0;
						ASSERT(fd->rd_on_full != 0);
						fd->rd_on_full(fd);
					}
				} else if ((sel_arg[i].got_flags & SEL_WRITE) && fd->wr_bufs[0].buf != 0) {
					size_t remain_size = fd->wr_bufs[0].size - fd->wr_bufs[0].written;
					void* write_ptr = fd->wr_bufs[0].buf + fd->wr_bufs[0].written;

					fd->wr_bufs[0].written += sc_write(fd->fd, 0, remain_size, write_ptr);

					if (fd->wr_bufs[0].written == fd->wr_bufs[0].size) {
						/*dbg_printf("(mainloop) finish write %d\n", fd->wr_bufs[0].size);*/
						if (fd->wr_bufs[0].must_free) free(fd->wr_bufs[0].buf);
						for (int i = 1; i < MAINLOOP_MAX_WR_BUFS; i++) {
							fd->wr_bufs[i-1] = fd->wr_bufs[i];
						}
						fd->wr_bufs[MAINLOOP_MAX_WR_BUFS-1].buf = 0;
					}
				}
				i++;
			}
			after_idle_step = false;
		}
	}
}

void mainloop_exit() {
	mainloop_must_exit = true;
}


/* vim: set ts=4 sw=4 tw=0 noet :*/