148 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "./tox_ipc_server.hpp"
 | |
| 
 | |
| #include "./zpp_rpc_cldev.hpp"
 | |
| 
 | |
| #include <tox/tox_events.h>
 | |
| 
 | |
| #include <span>
 | |
| #include <thread>
 | |
| #include <cstring>
 | |
| #include <iostream>
 | |
| 
 | |
| ToxIPCServer::ToxIPCServer(ToxI& t, std::mutex& t_m) : _t(t), _t_m(t_m) {
 | |
| }
 | |
| 
 | |
| ToxIPCServer::~ToxIPCServer(void) {
 | |
| 	ipc_mem_close(&_mem_rpc);
 | |
| 	ipc_sem_close(&_sem_rpc_c);
 | |
| 	ipc_sem_close(&_sem_rpc_s);
 | |
| 
 | |
| 	ipc_mem_close(&_mem_events);
 | |
| 	ipc_sem_close(&_sem_events_lock);
 | |
| }
 | |
| 
 | |
| bool ToxIPCServer::connect(void) {
 | |
| 	{ // rpc
 | |
| 		ipc_mem_init(&_mem_rpc, "solana_toxI_rpc.shm", RPC_MEM_SIZE); // 2k prob enough for now
 | |
| 		// server creates
 | |
| 		if (ipc_mem_create(&_mem_rpc) != 0) {
 | |
| 			std::cerr << "rpc shared mem already exists or failed\n";
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// client write lock (server waits on this)
 | |
| 		ipc_sem_init(&_sem_rpc_c, "solana_toxI_rpc_client.lock");
 | |
| 		if (ipc_sem_create(&_sem_rpc_c, 0) != 0) {
 | |
| 			std::cerr << "failed to create sem_rpc_c\n";
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// server write lock (client waits on this)
 | |
| 		ipc_sem_init(&_sem_rpc_s, "solana_toxI_rpc_server.lock");
 | |
| 		if (ipc_sem_create(&_sem_rpc_s, 0) != 0) {
 | |
| 			std::cerr << "failed to create sem_rpc_s\n";
 | |
| 			return false;
 | |
| 		}
 | |
| 		// if no call is in progress, both sems are 0
 | |
| 	}
 | |
| 
 | |
| 	{ // events
 | |
| 		ipc_mem_init(&_mem_events, "solana_toxEventsI_events.shm", EVENTS_MEM_SIZE);
 | |
| 		// server creates
 | |
| 		if (ipc_mem_create(&_mem_events) != 0) {
 | |
| 			std::cerr << "events shared mem already exists or failed\n";
 | |
| 			return false;
 | |
| 		}
 | |
| 		// TODO: do this with the lock?
 | |
| 		ipc_mem_access(&_mem_events)[0] = EVENTS_READ; // reset
 | |
| 
 | |
| 		// mutex
 | |
| 		ipc_sem_init(&_sem_events_lock, "solana_toxEventsI_events.lock");
 | |
| 		if (ipc_sem_create(&_sem_events_lock, 1) != 0) {
 | |
| 			std::cerr << "failed to create sem_events_lock\n";
 | |
| 			return false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| using namespace std::chrono_literals;
 | |
| 
 | |
| void ToxIPCServer::run(void) {
 | |
| 	while (!_quit) {
 | |
| 		// wait for client call
 | |
| 		ipc_sem_decrement(&_sem_rpc_c);
 | |
| 
 | |
| 		if (_quit) {
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		zpp::bits::in in{std::span{ipc_mem_access(&_mem_rpc), RPC_MEM_SIZE}};
 | |
| 		zpp::bits::out out{std::span{ipc_mem_access(&_mem_rpc), RPC_MEM_SIZE}};
 | |
| 
 | |
| 		ToxI_rpc::server se{in, out, _t}; // ephimeral, should be cheap
 | |
| 
 | |
| 		{
 | |
| 			std::lock_guard lg{_t_m};
 | |
| 			se.serve().or_throw();
 | |
| 		}
 | |
| 
 | |
| 		// signal client
 | |
| 		ipc_sem_increment(&_sem_rpc_s);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ToxIPCServer::start(void) {
 | |
| 	_thread = std::thread([this](){ this->run(); });
 | |
| }
 | |
| 
 | |
| void ToxIPCServer::stop(void) {
 | |
| 	_quit = true;
 | |
| 	ipc_sem_increment(&_sem_rpc_c); // hack, wake up server thread
 | |
| 	_thread.join();
 | |
| }
 | |
| 
 | |
| void ToxIPCServer::updateEvents(const Tox_Events* events) {
 | |
| 	if (uint32_t size = tox_events_bytes_size(events); size <= EVENTS_MEM_SIZE-1) {
 | |
| 		_tmp_events_buf.resize(size);
 | |
| 		tox_events_get_bytes(events, _tmp_events_buf.data());
 | |
| 	} else {
 | |
| 		std::cerr << "events data bigger than space (" << size << " > " << EVENTS_MEM_SIZE-1 << ")\n";
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ToxIPCServer::pub(void) {
 | |
| 	if (_tmp_events_buf.empty()) {
 | |
| 		return; // no events, no op
 | |
| 	}
 | |
| 
 | |
| 	// if noone is reading the events, this loop sleeps ~30ms
 | |
| 	for (size_t i = 0; i < 10; i++) {
 | |
| 		// grap mutex
 | |
| 		ipc_sem_decrement(&_sem_events_lock);
 | |
| 
 | |
| 		// look at first byte if it got read
 | |
| 		const uint8_t zbyte = ipc_mem_access(&_mem_events)[0];
 | |
| 		if (zbyte == EVENTS_WRITTEN) {
 | |
| 			// if not, release mutex and short sleep
 | |
| 			ipc_sem_increment(&_sem_events_lock);
 | |
| 			std::this_thread::sleep_for(3ms);
 | |
| 		} else {
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// after loop we write events (potentially overwriting)
 | |
| 
 | |
| 	// memcpy new events, set first byte
 | |
| 	std::memcpy(ipc_mem_access(&_mem_events)+1, _tmp_events_buf.data(), _tmp_events_buf.size());
 | |
| 	ipc_mem_access(&_mem_events)[0] = EVENTS_WRITTEN;
 | |
| 
 | |
| 	_tmp_events_buf.clear(); // TODO: just mark read, and keep around
 | |
| 
 | |
| 	// release mutex
 | |
| 	ipc_sem_increment(&_sem_events_lock);
 | |
| }
 | |
| 
 |