Regarding performance:
From lower-level embedded systems we may pick up the good practice "never use
memcpy
unless you actually have to". If the socket lib is already doing such through the API then that's already bad enough without the application adding further copying on top of that.Most often we can instead keep 2 or more buffers allocated (similar to
uint8_t Buf[MAX_TCP_FRAME];
) and just swap the pointers between them. Often there is no need to "zero out" old data either, but just to overwrite it.And regarding threading, it is good to minimize the time spent inside a mutex lock, since if we have lots of code there we are stalling other parts of the program. I would wildly guess that this part is the main bottleneck.
Though if you are to forward this data on someone else outside this module, there might seem to be no other way around it than hard copying. A different approach then could be to skip the local variable and instead pre-allocate a chunk using
malloc
so that the data will persist. Let the socket write directly into themalloc
buffer and then have the circular buffer work on pointers to allocated data instead of doing hard copies.malloc
does come with overhead, but if youmalloc
the next chunk just after receiving data, you may be able to time it so that you are doing malloc while the socket is still receiving the next incoming data packet.The standard nit-pick code review remark for structs is that
circular_buffer
is poorly optimized with regards to alignment. You shouldn't have a small type likebool
in the middle of a struct, put small types at the end to minimize the need for padding.
Regarding coding style:
The rule of thumb is to never use goto
because if you do, you have to suffer the old "goto considered harmful" debate yet again. Or as various industry coding standards usually put it: it's ok to use goto
for error handling, but only if you jump downwards.
In this specific case, goto
just makes readability worse. As does the while
loop even. The first loop could be replaced with an idiomatic, readable for
loop (and maybe less verbose names to get shorter lines, but that's a minor thing):
ssize_t total_read;ssize_t bytes_read = 0;for(total_read = 0; total_read < HEADER_SIZE; total_read += bytes_read){ bytes_read = recv(socket_fd, buffer + total_read, HEADER_SIZE - total_read, 0); // error handling code is never fun to read so best write it as clear as possible if(bytes_read == -1) { if(errno == EINTR) { bytes_read=0; /* ignore and continue */ } else { return -1; } } else if (bytes_read == 0) { return 0; }}...// next loop may have to skip the total_read initialization