void VoodooDispatcher::ProcessMessages( VoodooMessageHeader *first, size_t total_length ) { D_DEBUG_AT( Voodoo_Dispatcher, "VoodooDispatcher::%s( %p, first %p, total_length "_ZU" )\n", __func__, this, first, total_length ); D_MAGIC_ASSERT( this, VoodooDispatcher ); VoodooMessageHeader *header = first; size_t offset = 0; size_t aligned; while (offset < total_length) { /* Get the message header. */ header = (VoodooMessageHeader *)((char*) first + offset); aligned = VOODOO_MSG_ALIGN( header->size ); D_DEBUG_AT( Voodoo_Dispatcher, " -> Next message has %d ("_ZU") bytes and is of type %d... (offset "_ZU"/"_ZU")\n", header->size, aligned, header->type, offset, total_length ); D_ASSERT( header->size >= (int) sizeof(VoodooMessageHeader) ); D_ASSERT( header->size <= MAX_MSG_SIZE ); D_ASSERT( offset + aligned <= total_length ); switch (header->type) { case VMSG_SUPER: manager->handle_super( (VoodooSuperMessage*) header ); break; case VMSG_REQUEST: manager->handle_request( (VoodooRequestMessage*) header ); break; case VMSG_RESPONSE: manager->handle_response( (VoodooResponseMessage*) header ); break; case VMSG_DISCOVER: manager->handle_discover( header ); break; case VMSG_SENDINFO: // should only be received by TCP fake player, not manager D_BUG( "received SENDINFO" ); break; default: D_BUG( "invalid message type %d", header->type ); break; } offset += aligned; } D_ASSERT( offset == total_length ); }
VoodooPacket * VoodooConnectionLink::GetPacket( size_t length ) { D_DEBUG_AT( Voodoo_Connection, "VoodooConnectionLink::%s( %p, length "_ZU" )\n", __func__, this, length ); D_MAGIC_ASSERT( this, VoodooConnection ); D_ASSERT( length >= (int) sizeof(VoodooMessageHeader) ); D_ASSUME( length <= MAX_MSG_SIZE ); if (length > MAX_MSG_SIZE) { D_WARN( _ZU" exceeds maximum message size of %d", length, MAX_MSG_SIZE ); return NULL; } size_t aligned = VOODOO_MSG_ALIGN( length ); Packets *packets = (Packets*) direct_tls_get( output.tls ); if (!packets) { packets = new Packets( this ); direct_tls_set( output.tls, packets ); } VoodooPacket *packet = packets->active; if (packet) { if (packet->append( aligned )) return packet; Flush( packet ); } packet = packets->Get(); if (packet) { if (packet->sending) { direct_mutex_lock( &output.lock ); while (packet->sending) direct_waitqueue_wait( &output.wait, &output.lock ); direct_mutex_unlock( &output.lock ); } packet->reset( aligned ); } return packet; }
static DirectResult manager_lock_output( VoodooManager *manager, int length, void **ret_ptr ) { int aligned; D_MAGIC_ASSERT( manager, VoodooManager ); D_ASSERT( length >= sizeof(VoodooMessageHeader) ); D_ASSUME( length <= MAX_MSG_SIZE ); D_ASSERT( ret_ptr != NULL ); if (length > MAX_MSG_SIZE) { D_WARN( "%d exceeds maximum message size of %d", length, MAX_MSG_SIZE ); return DR_LIMITEXCEEDED; } aligned = VOODOO_MSG_ALIGN( length ); pthread_mutex_lock( &manager->output.lock ); while (manager->output.end + aligned > OUT_BUF_MAX) { pthread_cond_broadcast( &manager->output.wait ); pthread_cond_wait( &manager->output.wait, &manager->output.lock ); if (manager->quit) { pthread_mutex_lock( &manager->output.lock ); return DR_DESTROYED; } } *ret_ptr = manager->output.buffer + manager->output.end; manager->output.end += aligned; return DR_OK; }
void * VoodooConnectionPacket::io_loop() { D_DEBUG_AT( Voodoo_Connection, "VoodooConnectionPacket::%s( %p )\n", __func__, this ); while (!stop) { D_MAGIC_ASSERT( this, VoodooConnection ); if (input.start == input.max) { input.start = 0; input.end = 0; input.last = 0; input.max = VOODOO_CONNECTION_LINK_INPUT_BUF_MAX; } if (!stop) { DirectResult ret; VoodooChunk chunks[2]; VoodooChunk *chunk_read = NULL; VoodooChunk *chunk_write = NULL; size_t last = input.last; VoodooPacket *packet = NULL; std::vector<VoodooChunk> chunks_write; std::vector<VoodooChunk> chunks_read; if (!output.sending) { direct_mutex_lock( &output.lock ); if (output.packets) { VoodooPacket *packet = (VoodooPacket*) output.packets; D_ASSERT( packet->sending ); if (voodoo_config->compression_min && packet->size() >= voodoo_config->compression_min) { output.sending = VoodooPacket::Compressed( packet ); if (output.sending->flags() & VPHF_COMPRESSED) { D_DEBUG_AT( Voodoo_Output, " -> Compressed %u to %u bytes... (packet %p)\n", output.sending->uncompressed(), output.sending->size(), packet ); output.sending->sending = true; packet->sending = false; direct_list_remove( &output.packets, &packet->link ); direct_waitqueue_broadcast( &output.wait ); } } else output.sending = packet; output.sent = 0; } direct_mutex_unlock( &output.lock ); } if (output.sending) { packet = output.sending; D_ASSERT( packet->sending ); chunk_write = &chunks[1]; chunk_write->ptr = (char*) packet->data_header() + output.sent; chunk_write->length = VOODOO_MSG_ALIGN(packet->size() + sizeof(VoodooPacketHeader)) - output.sent; chunk_write->done = 0; chunks_write.push_back( chunks[1] ); chunk_write = chunks_write.data(); } if (input.end < input.max && manager->DispatchReady()) { chunk_read = &chunks[0]; chunk_read->ptr = input.buffer + input.end; chunk_read->length = input.max - input.end; chunk_read->done = 0; chunks_read.push_back( chunks[0] ); chunk_read = chunks_read.data(); } ret = link->SendReceive( link, chunks_write.data(), chunks_write.size(), chunks_read.data(), chunks_read.size() ); switch (ret) { case DR_OK: if (chunk_write && chunk_write->done) { D_DEBUG_AT( Voodoo_Output, " -> Sent "_ZD"/"_ZD" bytes... (packet %p)\n", chunk_write->done, chunk_write->length, packet ); output.sent += chunk_write->done; if (output.sent == VOODOO_MSG_ALIGN(packet->size() + sizeof(VoodooPacketHeader))) { output.sending = NULL; if (packet->flags() & VPHF_COMPRESSED) { packet->sending = false; D_FREE( packet ); } else { direct_mutex_lock( &output.lock ); packet->sending = false; direct_list_remove( &output.packets, &packet->link ); direct_mutex_unlock( &output.lock ); direct_waitqueue_broadcast( &output.wait ); } } } break; case DR_TIMEOUT: //D_DEBUG_AT( Voodoo_Connection, " -> timeout\n" ); break; case DR_INTERRUPTED: D_DEBUG_AT( Voodoo_Connection, " -> interrupted\n" ); break; default: if (ret == DR_IO) D_DEBUG_AT( Voodoo_Connection, " -> Connection closed!\n" ); else D_DERROR( ret, "Voodoo/ConnectionPacket: Could not receive data!\n" ); goto disconnect; } if (chunk_read && chunk_read->done) { D_DEBUG_AT( Voodoo_Input, " -> Received "_ZD" bytes...\n", chunk_read->done ); input.end += (size_t) chunk_read->done; do { VoodooPacketHeader *header; size_t aligned; D_DEBUG_AT( Voodoo_Input, " -> last = %zu\n", last ); /* Get the packet header. */ header = (VoodooPacketHeader *)(input.buffer + last); D_DEBUG_AT( Voodoo_Input, " -> header = %p\n", header ); D_DEBUG_AT( Voodoo_Input, " -> header->size = %u\n", header->size ); aligned = VOODOO_MSG_ALIGN( header->size ); D_DEBUG_AT( Voodoo_Input, " -> Next packet has %u ("_ZU") -> %u bytes (flags 0x%04x)...\n", header->size, aligned, header->uncompressed, header->flags ); if (input.end - last >= sizeof(VoodooPacketHeader)) { if (header->uncompressed < (int) sizeof(VoodooMessageHeader)) { D_DERROR( ret, "Voodoo/ConnectionPacket: Data error, uncompressed %d < min %zu!\n", header->uncompressed, sizeof(VoodooPacketHeader) ); goto disconnect; } if (header->uncompressed > VOODOO_PACKET_MAX) { D_DERROR( ret, "Voodoo/ConnectionPacket: Data error, uncompressed %d > max %d!\n", header->uncompressed, VOODOO_PACKET_MAX ); goto disconnect; } } if (sizeof(VoodooPacketHeader) + aligned > input.end - last) { D_DEBUG_AT( Voodoo_Input, " -> ...fetching tail of message.\n" ); /* Extend the buffer if the message doesn't fit into the default boundary. */ if (sizeof(VoodooPacketHeader) + aligned > input.max - last) input.max = last + sizeof(VoodooPacketHeader) + aligned; break; } last += sizeof(VoodooPacketHeader) + aligned; } while (last < input.end); if (last != input.last) { input.last = last; D_DEBUG_AT( Voodoo_Input, " { START "_ZD", LAST "_ZD", END "_ZD", MAX "_ZD" }\n", input.start, input.last, input.end, input.max ); while (input.start < input.last) { /* Get the packet header. */ VoodooPacketHeader *header = (VoodooPacketHeader *)(input.buffer + input.start); VoodooPacket *p; D_ASSERT( header->uncompressed <= VOODOO_PACKET_MAX ); if (header->flags & VPHF_COMPRESSED) { size_t uncompressed = direct_fastlz_decompress( header + 1, header->size, tmp, header->uncompressed ); D_DEBUG_AT( Voodoo_Input, " -> Uncompressed "_ZU" bytes (%u compressed)\n", uncompressed, header->size ); (void) uncompressed; D_ASSERT( uncompressed == header->uncompressed ); // FIXME: don't copy, but read into packet directly, maybe call manager->GetPacket() at the top of this loop p = VoodooPacket::Copy( header->uncompressed, VPHF_NONE, header->uncompressed, tmp ); } else { // FIXME: don't copy, but read into packet directly, maybe call manager->GetPacket() at the top of this loop p = VoodooPacket::Copy( header->uncompressed, VPHF_NONE, header->uncompressed, header + 1 ); } manager->DispatchPacket( p ); input.start += VOODOO_MSG_ALIGN(header->size) + sizeof(VoodooPacketHeader); } } } } } return NULL; disconnect: closed = true; manager->handle_disconnect(); return NULL; }
static inline int calc_blocks( VoodooMessageBlockType type, va_list args ) { int size = 4; /* for the terminating VMBT_NONE */ while (type != VMBT_NONE) { u32 arg; s32 in; void *ptr; int len = 0; switch (type) { case VMBT_ID: len = 4; arg = va_arg( args, u32 ); D_DEBUG( "Voodoo/Message: + ID %u\n", arg ); break; case VMBT_INT: len = 4; in = va_arg( args, s32 ); D_DEBUG( "Voodoo/Message: + INT %d\n", in ); break; case VMBT_UINT: len = 4; arg = va_arg( args, u32 ); D_DEBUG( "Voodoo/Message: + UINT %u\n", arg ); break; case VMBT_DATA: len = va_arg( args, int ); ptr = va_arg( args, void * ); D_ASSERT( len > 0 ); D_ASSERT( ptr != NULL ); D_DEBUG( "Voodoo/Message: + DATA at %p with length %d\n", ptr, len ); break; case VMBT_ODATA: len = va_arg( args, int ); ptr = va_arg( args, void * ); D_ASSERT( len > 0 ); D_DEBUG( "Voodoo/Message: + ODATA at %p with length %d\n", ptr, len ); if (!ptr) len = 0; break; case VMBT_STRING: ptr = va_arg( args, char * ); len = strlen( ptr ) + 1; D_ASSERT( ptr != NULL ); D_DEBUG( "Voodoo/Message: + STRING '%s' at %p with length %d\n", (char*) ptr, ptr, len ); break; default: D_BREAK( "unknown message block type" ); } size += 8 + VOODOO_MSG_ALIGN(len); type = va_arg( args, VoodooMessageBlockType ); } return size; }
static void * manager_dispatch_loop( DirectThread *thread, void *arg ) { VoodooManager *manager = arg; /* Lock the input buffer. */ pthread_mutex_lock( &manager->input.lock ); while (!manager->quit) { VoodooMessageHeader *header; int aligned; D_MAGIC_ASSERT( manager, VoodooManager ); D_DEBUG( "Voodoo/Dispatch: START %d, END %d, MAX %d\n", manager->input.start, manager->input.end, manager->input.max ); /* Wait for at least four bytes which contain the length of the message. */ while (manager->input.end - manager->input.start < 4) { D_DEBUG( "Voodoo/Dispatch: Waiting for messages...\n" ); pthread_cond_wait( &manager->input.wait, &manager->input.lock ); if (manager->quit) break; } if (manager->quit) break; /* Get the message header. */ header = (VoodooMessageHeader *)(manager->input.buffer + manager->input.start); aligned = VOODOO_MSG_ALIGN( header->size ); D_DEBUG( "Voodoo/Dispatch: Next message has %d (%d) bytes and is of type %d...\n", header->size, aligned, header->type ); D_ASSERT( header->size >= sizeof(VoodooMessageHeader) ); D_ASSERT( header->size <= MAX_MSG_SIZE ); /* Extend the buffer if the message doesn't fit into the default boundary. */ if (aligned > manager->input.max - manager->input.start) { D_ASSERT( manager->input.max == IN_BUF_MAX ); D_DEBUG( "Voodoo/Dispatch: ...fetching tail of message.\n" ); manager->input.max = manager->input.start + aligned; pthread_cond_broadcast( &manager->input.wait ); } /* Wait until the complete message is received. */ while (aligned > manager->input.end - manager->input.start) { pthread_cond_wait( &manager->input.wait, &manager->input.lock ); if (manager->quit) break; } if (manager->quit) break; /* Unlock the input buffer. */ pthread_mutex_unlock( &manager->input.lock ); switch (header->type) { case VMSG_SUPER: handle_super( manager, (VoodooSuperMessage*) header ); break; case VMSG_REQUEST: handle_request( manager, (VoodooRequestMessage*) header ); break; case VMSG_RESPONSE: handle_response( manager, (VoodooResponseMessage*) header ); break; default: D_BUG( "invalid message type %d", header->type ); break; } /* Lock the input buffer. */ pthread_mutex_lock( &manager->input.lock ); manager->input.start += aligned; if (manager->input.start == manager->input.end) { if (manager->input.start == manager->input.max) pthread_cond_broadcast( &manager->input.wait ); manager->input.start = manager->input.end = 0; manager->input.max = IN_BUF_MAX; } } /* Unlock the input buffer. */ pthread_mutex_unlock( &manager->input.lock ); return NULL; }