new makefile

This commit is contained in:
cozis
2023-10-31 16:09:51 +01:00
parent 7033e1ea85
commit b495e53891
42 changed files with 1648 additions and 434 deletions
Submodule 3p/packetdrill deleted from 8595c94c9c
Submodule 3p/tinycthread deleted from 6957fc8383
+18 -4
View File
@@ -4,7 +4,18 @@ MicroTCP is a TCP/IP network stack which I started building as a learning exerci
At this moment MicroTCP implements ARP (RFC 826, complete), IPv4 (no fragmentation), ICMP (minimum necessary to reply to pings) and TCP (complete but not stress-tested). Note that "complete" should be not intended as "fully compliant" but just as a measure of progress on all of the major features. For instance, it's complete enough to handle HTTP traffic on a local network (Look into examples/microhttp to know more). At this moment MicroTCP implements ARP (RFC 826, complete), IPv4 (no fragmentation), ICMP (minimum necessary to reply to pings) and TCP (complete but not stress-tested). Note that "complete" should be not intended as "fully compliant" but just as a measure of progress on all of the major features. For instance, it's complete enough to handle HTTP traffic on a local network (Look into examples/microhttp to know more).
## Where does it run? ## Where does it run?
MicroTCP can run on Windows and Linux alongside the OS's network stack. To route the network traffic to MicroTCP, the process running it behaves as a virtual host with its own IP address. This is done using a TAP device, which comes built-in on Linux but needs installing on Windows. It should be very easy to adapt to run on microcontrollers but haven't tried yet, the dream is to serve my [blog](https://cozis.github.io/) from an STM32 board. MicroTCP can run on Windows and Linux alongside the OS's network stack. To route the network traffic to MicroTCP, the process running it behaves as a virtual host with its own IP address. This is done using a TAP device, which comes built-in on Linux but needs installing on Windows. It should be very easy to adapt to run MicroTCP on microcontrollers but haven't tried yet. The dream is to serve my [blog](https://cozis.github.io/) from an STM32 board!
## Build and Install
If you are on Windows, you need to install the TAP driver provided by OpenVPN and instanciate a virtual NIC so that MicroTCP can connect to it when started. To build the project from source, make sure you cloned the repository with submodules
```sh
git clone https://github.com/cozis/microtcp.git --recursive
```
and then run
```sh
make
```
You'll need both `make` and `cmake` for it to work. If all goes well, you'll find the library files `libtuntap.a`, `libmicrotcp.a` and header files `tuntap.h`, `tuntap-export.h`, `microtcp.h` in `out/`.
## Usage ## Usage
MicroTCP's uses the usual socket interface any network programmer is familiar with, the main difference being you need to esplicitly instanciate the network stack and pass its handle around. Without further ado, here's a simple echo server that shows the basic usage: MicroTCP's uses the usual socket interface any network programmer is familiar with, the main difference being you need to esplicitly instanciate the network stack and pass its handle around. Without further ado, here's a simple echo server that shows the basic usage:
@@ -42,8 +53,11 @@ int main(void)
``` ```
This should be pretty straight forward to understand. One thing may be worth noting is that `microtcp_open` behaves as the BSD's `socket+bind+listen` all at once to setup a listening TCP server. This should be pretty straight forward to understand. One thing may be worth noting is that `microtcp_open` behaves as the BSD's `socket+bind+listen` all at once to setup a listening TCP server.
There are more than one way to set up the stack, the main way being `microtcp_create` which creates a virtual network inferface on the host OS with IP 10.0.0.5/24 and a virtual host for the MicroTCP process with the 10.0.0.4/24 IP. You can open Wireshark on the virtual NIC to inspect the traffic between the host and the process. There is more than one way to set up the stack, the main way being `microtcp_create` which creates a virtual network inferface on the host OS with IP 10.0.0.5/24 and a virtual host for the MicroTCP process at 10.0.0.4/24. You can open Wireshark on the virtual NIC to inspect the traffic between the host and the process.
It's also possible to configure the stack using the `microtcp_create_using_callbacks`, which lets you explicitly provide the input L2 frames to it and receive the frames in a buffer. This is how one would run the stack on a microcontroller. It's also possible to configure the stack using the `microtcp_create_using_callbacks`, which lets you explicitly provide the input L2 frames to it and receive the frames in a buffer. This is how one would configure the stack to run on a microcontroller.
Each instance of MicroTCP (without considering the callbacks) is completely isolated from the others, therefore, if your specific callback implementation allows it, you can have as many instances as you like! Usually the callbacks introduce a dependency between the stacks because the system is one big global state. Each instance of MicroTCP (without considering the callbacks) is completely isolated from the others, therefore, if your specific callback implementation allows it, you can have as many instances as you like!
## Testing
There is still no testing infractructure. The way I'm testing it is by setting up an HTTP or echo server and stressing it until something breaks while capturing what happened using Wireshark.
+18 -3
View File
@@ -3,13 +3,28 @@
int main(void) int main(void)
{ {
microtcp_errcode_t err;
microtcp_t *mtcp = microtcp_create("10.0.0.5", "10.0.0.4", NULL, NULL); microtcp_t *mtcp = microtcp_create("10.0.0.5", "10.0.0.4", NULL, NULL);
if (mtcp == NULL) {
fprintf(stderr, "Error: Couldn't create MicroTCP instance\n");
return -1;
}
uint16_t port = 8081; uint16_t port = 8081;
microtcp_socket_t *server = microtcp_open(mtcp, port, NULL); microtcp_socket_t *server = microtcp_open(mtcp, port, &err);
if (server == NULL) {
fprintf(stderr, "Error: %s\n", microtcp_strerror(err));
microtcp_destroy(mtcp);
return -1;
}
while (1) { while (1) {
microtcp_socket_t *client = microtcp_accept(server, false, NULL); microtcp_socket_t *client = microtcp_accept(server, false, &err);
if (client == NULL) {
fprintf(stderr, "Error: %s\n", microtcp_strerror(err));
break;
}
char buffer[1024]; char buffer[1024];
size_t num = microtcp_recv(client, buffer, sizeof(buffer), false, NULL); size_t num = microtcp_recv(client, buffer, sizeof(buffer), false, NULL);
microtcp_send(client, "echo: ", 6, false, NULL); microtcp_send(client, "echo: ", 6, false, NULL);
+58 -120
View File
@@ -1,147 +1,85 @@
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
OSNAME = windows OSTAG = WIN
ifeq ($(PROCESSOR_ARCHITEW6432),AMD64) EXT = .exe
ARCH = AMD64 CMAKE_GENERATOR = "MinGW Makefiles"
else ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
ARCH = AMD64
else ifeq ($(PROCESSOR_ARCHITECTURE),x86)
ARCH = IA32
endif
else else
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux) ifeq ($(UNAME_S),Linux)
OSNAME = linux OSTAG = LIN
EXT =
CMAKE_GENERATOR = "Unix Makefiles"
else ifeq ($(UNAME_S),Darwin) else ifeq ($(UNAME_S),Darwin)
$(error OSX isn't supported)
OSNAME = osx OSNAME = osx
endif endif
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_P),x86_64)
ARCH = AMD64
endif
ifneq ($(filter %86,$(UNAME_P)),)
ARCH = IA32
endif
ifneq ($(filter arm%,$(UNAME_P)),)
ARCH = ARM
endif
endif endif
ifeq ($(OSNAME),windows) AR = ar$(EXT)
CC = gcc.exe CC = gcc$(EXT)
CXX = g++.exe CXX = g++$(EXT)
CFLAGS_PLATFORM =
LFLAGS_PLATFORM = -lws2_32
CMAKE_GENERATOR = "MinGW Makefiles"
else ifeq ($(OSNAME),linux)
CC = gcc
CXX = g++
CFLAGS_PLATFORM = -pthread
LFLAGS_PLATFORM =
CMAKE_GENERATOR = "Unix Makefiles"
else
endif
# One of: drmemory, valgrind SRCDIR = src
MEMDBG=valgrind OBJDIR = obj
OUTDIR = out
LIBDIR = 3p/lib CFILES = $(wildcard $(SRCDIR)/*.c)
INCDIR = 3p/include HFILES = $(wildcard $(SRCDIR)/*.h)
OFILES = $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(CFILES))
CFLAGS = $(CUSTOM_CFLAGS) $(CFLAGS_PLATFORM) -I$(INCDIR) -Ibuild/ -Wall -Wextra CFLAGS_WIN =
LFLAGS = $(CUSTOM_LFLAGS) -ltuntap $(LFLAGS_PLATFORM) -L$(LIBDIR) CFLAGS_LIN = -pthread
CFLAGS = -Wall -Wextra -g $(CFLAGS_$(OSTAG)) -DMICROTCP_DEBUG -DTCP_DEBUG
ifeq ($(MEMDBG),drmemory) LIBFILE = $(OUTDIR)/libmicrotcp.a
CFLAGS += -gdwarf-2
else
CFLAGS += -g3
endif
.PHONY: all clean TAP_AFILES = $(OUTDIR)/libtuntap.a
TAP_HFILES = $(OUTDIR)/tuntap.h $(OUTDIR)/tuntap-export.h
all: build/microtcp.h build/microtcp.c build/echo_tcp build/echo_http build/test EXAMPLE_0 = $(OUTDIR)/http$(EXT)
EXAMPLE_1 = $(OUTDIR)/echo$(EXT)
3p/lib/libtuntap.a: 3p/libtuntap/build/lib/libtuntap.a EXAMPLE_CFILES_0 = $(wildcard examples/http/*.c)
cp 3p/libtuntap/build/lib/libtuntap.a $(LIBDIR) EXAMPLE_HFILES_0 = $(wildcard examples/http/*.h)
3p/include/tuntap.h: 3p/libtuntap/tuntap.h EXAMPLE_CFILES_1 = examples/echo.c
cp 3p/libtuntap/tuntap.h $(INCDIR) EXAMPLE_HFILES_1 =
3p/include/tuntap-export.h: 3p/libtuntap/build/tuntap-export.h EXAMPLE_LFLAGS_WIN = -lws2_32
cp 3p/libtuntap/build/tuntap-export.h $(INCDIR) EXAMPLE_LFLAGS_LIN =
EXAMPLE_LFLAGS = -lmicrotcp -ltuntap $(EXAMPLE_LFLAGS_$(OSTAG))
3p/libtuntap/build/lib/libtuntap.a 3p/libtuntap/tuntap.h 3p/libtuntap/build/tuntap-export.h: .PHONY: all exm lib clean
cd 3p/libtuntap/ \
&& mkdir -p build \
&& cd build \
&& cmake .. -G $(CMAKE_GENERATOR) \
-DBUILD_TESTING=OFF \
-DCMAKE_C_COMPILER=$(CC) \
-DCMAKE_CXX_COMPILER=$(CXX) \
-DCMAKE_BUILD_TYPE=Debug \
&& make
3p/include/tinycthread.h: 3p/tinycthread/source/tinycthread.h all: lib exm
cp 3p/tinycthread/source/tinycthread.h 3p/include
3p/src/tinycthread.c: 3p/tinycthread/source/tinycthread.c exm: $(EXAMPLE_0) $(EXAMPLE_1)
cp 3p/tinycthread/source/tinycthread.c 3p/src
build/echo_tcp: examples/echo_tcp.c $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.c build/microtcp.h lib: $(LIBFILE) $(TAP_HFILES) $(TAP_AFILES)
mkdir -p $(@D) cp $(SRCDIR)/microtcp.h $(OUTDIR)/microtcp.h
gcc build/microtcp.c examples/echo_tcp.c -o $@ $(CFLAGS) $(LFLAGS) -DDEBUG=1 -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_USING_TAP -DMICROTCP_USING_MUX
build/microtcp.h: src/microtcp.h $(OBJDIR)/%.o: $(SRCDIR)/%.c $(HFILES) $(TAP_HFILES)
mkdir -p $(@D) $(CC) -c -o $@ $< $(CFLAGS) -I$(OUTDIR)
[ ! -e $@ ] || rm $@
echo "#define MICROTCP_AMALGAMATION" >> $@
cat src/microtcp.h >> $@
build/microtcp.c: 3p/include/tinycthread.h 3p/src/tinycthread.c $(wildcard src/*.c src/*.h) $(LIBFILE): $(OFILES)
mkdir -p $(@D) $(AR) rcs $@ $^
[ ! -e $@ ] || rm $@
printf "#include \"microtcp.h\"\n" > $@
printf "#ifdef MICROTCP_BACKGROUND_THREAD" >> $@
printf "\n#line 1 \"3p/include/tinycthread.h\"\n" >> $@
cat 3p/include/tinycthread.h >> $@
printf "\n#line 1 \"3p/src/tinycthread.c\"\n" >> $@
cat 3p/src/tinycthread.c >> $@
printf "\n#endif /* MICROTCP_BACKGROUND_THREAD */" >> $@
printf "\n#line 1 \"src/defs.h\"\n" >> $@
cat src/defs.h >> $@
printf "\n#line 1 \"src/utils.h\"\n" >> $@
cat src/utils.h >> $@
printf "\n#line 1 \"src/endian.h\"\n" >> $@
cat src/endian.h >> $@
printf "\n#line 1 \"src/arp.h\"\n" >> $@
cat src/arp.h >> $@
printf "\n#line 1 \"src/icmp.h\"\n" >> $@
cat src/icmp.h >> $@
printf "\n#line 1 \"src/ip.h\"\n" >> $@
cat src/ip.h >> $@
printf "\n#line 1 \"src/tcp_timer.h\"\n" >> $@
cat src/tcp_timer.h >> $@
printf "\n#line 1 \"src/tcp.h\"\n" >> $@
cat src/tcp.h >> $@
printf "\n#line 1 \"src/utils.c\"\n" >> $@
cat src/utils.c >> $@
printf "\n#line 1 \"src/endian.c\"\n" >> $@
cat src/endian.c >> $@
printf "\n#line 1 \"src/arp.c\"\n" >> $@
cat src/arp.c >> $@
printf "\n#line 1 \"src/icmp.c\"\n" >> $@
cat src/icmp.c >> $@
printf "\n#line 1 \"src/ip.c\"\n" >> $@
cat src/ip.c >> $@
printf "\n#line 1 \"src/tcp_timer.c\"\n" >> $@
cat src/tcp_timer.c >> $@
printf "\n#line 1 \"src/tcp.c\"\n" >> $@
cat src/tcp.c >> $@
printf "\n#line 1 \"src/microtcp.c\"\n" >> $@
cat src/microtcp.c >> $@
build/echo_http: examples/microhttp/main.c $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.h build/microtcp.c $(TAP_AFILES) $(TAP_HFILES):
gcc examples/microhttp/main.c examples/microhttp/xhttp.c build/microtcp.c -o $@ $(CFLAGS) $(LFLAGS) -DDEBUG=1 -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_USING_MUX cd 3p/libtuntap/ && \
mkdir -p build && \
cd build && \
cmake .. -G $(CMAKE_GENERATOR) -DBUILD_TESTING=OFF -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) -DCMAKE_BUILD_TYPE=Debug && \
make
cp 3p/libtuntap/build/lib/libtuntap.a $(OUTDIR)/libtuntap.a
cp 3p/libtuntap/tuntap.h $(OUTDIR)/tuntap.h
cp 3p/libtuntap/build/tuntap-export.h $(OUTDIR)/tuntap-export.h
$(EXAMPLE_0): lib $(EXAMPLE_CFILES_0) $(EXAMPLE_HFILES_0)
$(CC) -o $@ $(EXAMPLE_CFILES_0) $(CFLAGS) $(EXAMPLE_LFLAGS) -I$(OUTDIR) -L$(OUTDIR)
$(EXAMPLE_1): lib $(EXAMPLE_CFILES_1) $(EXAMPLE_HFILES_1)
$(CC) -o $@ $(EXAMPLE_CFILES_1) $(CFLAGS) $(EXAMPLE_LFLAGS) -I$(OUTDIR) -L$(OUTDIR)
clean: clean:
rm -fr build 3p/libtuntap/build 3p/lib/* 3p/include/* rm -fr $(OBJDIR) $(OUTDIR)
mkdir obj out
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+82
View File
@@ -0,0 +1,82 @@
#ifndef MICROTCP_H
#define MICROTCP_H
#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <stdbool.h>
typedef struct microtcp_t microtcp_t;
typedef struct microtcp_socket_t microtcp_socket_t;
#define MICROTCP_MAX_BUFFERS 8
#define MICROTCP_MAX_SOCKETS 32
#define MICROTCP_MAX_MUX_ENTRIES 32
typedef enum {
MICROTCP_ERRCODE_NONE = 0,
// Returned by microtcp_open and microtcp_accept
MICROTCP_ERRCODE_SOCKETLIMIT,
// Returned by microtcp_open
MICROTCP_ERRCODE_TCPERROR,
MICROTCP_ERRCODE_BADCONDVAR,
// Returned by microtcp_accept
MICROTCP_ERRCODE_NOTLISTENER,
MICROTCP_ERRCODE_CANTBLOCK,
// Returned by microtcp_recv and microtcp_send
MICROTCP_ERRCODE_NOTCONNECTION,
// Returned by microtcp_accept, microtcp_recv and microtcp_send
MICROTCP_ERRCODE_WOULDBLOCK,
// Returned by microtcp_recv, microtcp_send
MICROTCP_ERRCODE_PEERCLOSED,
} microtcp_errcode_t;
typedef struct {
void *data;
void (*free)(void *data);
int (*send)(void *data, const void *src, size_t len);
int (*recv)(void *data, void *dst, size_t len);
} microtcp_callbacks_t;
bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac, microtcp_callbacks_t *callbacks);
microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip, const char *tap_mac, const char *stack_mac);
microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_callbacks_t callbacks);
void microtcp_destroy(microtcp_t *mtcp);
const char *microtcp_strerror(microtcp_errcode_t errcode);
microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port, microtcp_errcode_t *errcode);
microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, bool no_block, microtcp_errcode_t *errcode);
void microtcp_close(microtcp_socket_t *socket);
size_t microtcp_send(microtcp_socket_t *socket, const void *src, size_t len, bool no_block, microtcp_errcode_t *errcode);
size_t microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len, bool no_block, microtcp_errcode_t *errcode);
void microtcp_step(microtcp_t *mtcp);
void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len);
typedef enum {
MICROTCP_MUX_ACCEPT = 1,
MICROTCP_MUX_RECV = 2,
MICROTCP_MUX_SEND = 4,
} microtcp_muxeventid_t;
typedef struct {
void *userp;
int events;
microtcp_socket_t *socket;
} microtcp_muxevent_t;
typedef struct microtcp_mux_t microtcp_mux_t;
microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp);
void microtcp_mux_destroy(microtcp_mux_t *mux);
bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp);
bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events);
bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev);
#endif
-119
View File
@@ -1,125 +1,6 @@
/* Il protocollo ARP (Address Resolution Protocol)
* permette di tradurre gli indirizzi di livello
* rete (come IP) a quelli del livello inferiore
* data-link (come ethernet).
* Per grandi linee, i messaggi ARP possono essere
* REQUEST o REPLY. Quando un host vuole comunicare
* con un altro host dato il suo IP (o qualsiasi
* indirizzo di livello 3), manda un messaggio di
* REQUEST in broadcast (MAC di destinazione
* ff:ff:ff:ff:ff:ff) contenente l'indirizzo di
* livello 3 del quale si vuole conoscere quello di
* livello 2. Ciascun host della rete riceve il
* messaggio e controlla se la richiesta è relativa
* al proprio IP. Se il controllo risulta positivo
* risponde con un messaggio di REPLY contenente
* il proprio MAC. Il messaggio di REPLY, a differenza
* della REQUEST è un unicast dato che è noto il
* destinatario.
*
* Il messaggio ARP è grande 28 byte ed, indipendentemente
* dal tipo di richiesta, ha questa struttura:
*
* (16 bits per row)
* +-----------------------------------+
* 0 | hardware_type |
* +-----------------------------------+
* 2 | protocol_type |
* +-----------------+-----------------+
* 4 | hardware_length | protocol_length |
* +-----------------+-----------------+
* 6 | operation_type |
* +-----------------------------------+
* 8 | sender_hardware_address |
* +- - - - - - - - - - - - - - - - - -+
* | |
* +- - - - - - - - - - - - - - - - - -+
* | |
* +-----------------------------------+
* 14 | sender_protocol_address |
* +- - - - - - - - - - - - - - - - - -+
* | |
* +-----------------------------------+
* 18 | target_hardware_address |
* +- - - - - - - - - - - - - - - - - -+
* | |
* +- - - - - - - - - - - - - - - - - -+
* | |
* +-----------------------------------+
* 24 | target_protocol_address |
* +- - - - - - - - - - - - - - - - - -+
* | |
* +-----------------------------------+
*
* I campi hardware_type e protocol_type indicano
* il protocollo di livello 2 e quello di livello 3.
* Nel caso di IP su Ethernet si ha hardware_type=1
* e protocol_type=0x800.
*
* I campi hardware_length e protocol_length
* indicano la dimensione in byte degli indirizzi
* dei due protocolli. Per IP e Ethernet questi
* sono ridondanti dato che ogni indirizzo Ethernet
* è di 6 byte ed ogni indirizzo IP di 4.
*
* Il campo operation_type indica la finalità del
* messaggio. Può essere uno di:
* ARP REQUEST -> operation_type=1
* ARP REPLY -> operation_type=2
* RARP REQUEST -> operation_type=3
* RARP REPLY -> operation_type=4
* (possiamo ignorare RARP per ora)
*
* Il campo sender_hardware_address e sender_protocol_address
* contengono MAC e IP di chi ha inviato il messaggio.
*
* Il campo target_hardware_address cambia di significato
* a seconda del tipo di operazione:
* ARP REQUEST -> è vuoto, perchè non è noto il MAC di
* destinazione (vogliamo determinarlo)
* ARP REPLY -> indirizzo MAC di chi ha fatto la REQUEST
*
* Il campo target_protocol_address contiene l'indirizzo IP
* di chi ha inviato il messaggio.
*/
/*
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4;
unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4;
unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
uint8_t tos;
uint16_t tot_len;
uint16_t id;
uint16_t frag_off;
uint8_t ttl;
uint8_t protocol;
uint16_t check;
uint32_t saddr;
uint32_t daddr;
// The options start here.
};
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; // destination eth addr
unsigned char h_source[ETH_ALEN]; // source ether addr
__be16 h_proto; // packet type ID field
} __attribute__((packed));
*/
#include <stdbool.h> #include <stdbool.h>
#ifndef MICROTCP_AMALGAMATION
#include "endian.h" #include "endian.h"
#include "arp.h" #include "arp.h"
#endif
#ifdef ARP_DEBUG #ifdef ARP_DEBUG
#include <stdio.h> #include <stdio.h>
-3
View File
@@ -1,10 +1,7 @@
#include <assert.h> #include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#ifndef MICROTCP_AMALGAMATION
#include "defs.h" #include "defs.h"
#endif
#define ARP_MAX_PENDING_REQUESTS 32 #define ARP_MAX_PENDING_REQUESTS 32
#define ARP_TRANSLATION_TABLE_SIZE 128 #define ARP_TRANSLATION_TABLE_SIZE 128
-2
View File
@@ -1,6 +1,4 @@
#ifndef MICROTCP_AMALGAMATION
#include "endian.h" #include "endian.h"
#endif
bool cpu_is_little_endian(void) bool cpu_is_little_endian(void)
{ {
-3
View File
@@ -1,9 +1,6 @@
#include <string.h> #include <string.h>
#ifndef MICROTCP_AMALGAMATION
#include "endian.h" #include "endian.h"
#include "icmp.h" #include "icmp.h"
#endif
#ifdef ICMP_DEBUG #ifdef ICMP_DEBUG
#include <stdio.h> #include <stdio.h>
-3
View File
@@ -1,8 +1,5 @@
#include <stddef.h> #include <stddef.h>
#ifndef MICROTCP_AMALGAMATION
#include "defs.h" #include "defs.h"
#endif
typedef struct { typedef struct {
void *output_ptr; void *output_ptr;
-3
View File
@@ -1,9 +1,6 @@
#include <string.h> #include <string.h>
#ifndef MICROTCP_AMALGAMATION
#include "endian.h" #include "endian.h"
#include "ip.h" #include "ip.h"
#endif
#ifdef IP_DEBUG #ifdef IP_DEBUG
#include <stdio.h> #include <stdio.h>
-3
View File
@@ -1,11 +1,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#ifndef MICROTCP_AMALGAMATION
#include "defs.h" #include "defs.h"
#include "icmp.h" #include "icmp.h"
#endif
#define IP_PLUGGED_PROTOCOLS_MAX 4 #define IP_PLUGGED_PROTOCOLS_MAX 4
+55 -147
View File
@@ -3,22 +3,14 @@
#include <string.h> // strerror() #include <string.h> // strerror()
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#ifndef MICROTCP_AMALGAMATION
# include "ip.h"
# include "arp.h"
# include "tcp.h"
# include "utils.h"
# include "endian.h"
# include "microtcp.h"
# ifdef MICROTCP_BACKGROUND_THREAD
# include "tinycthread.h"
# endif
#endif
#ifdef MICROTCP_USING_TAP
#include <tuntap.h> #include <tuntap.h>
#endif #include "ip.h"
#include "arp.h"
#include "tcp.h"
#include "utils.h"
#include "endian.h"
#include "microtcp.h"
#include "tinycthread.h"
#ifdef MICROTCP_DEBUG #ifdef MICROTCP_DEBUG
#include <stdio.h> #include <stdio.h>
@@ -27,15 +19,7 @@
#define MICROTCP_DEBUG_LOG(...) do {} while (0); #define MICROTCP_DEBUG_LOG(...) do {} while (0);
#endif #endif
#ifdef MICROTCP_BACKGROUND_THREAD
#define LOCK_WHEN_THREADED(mtcp) do { mtx_lock(&(mtcp)->lock); } while (0);
#define UNLOCK_WHEN_THREADED(mtcp) do { mtx_unlock(&(mtcp)->lock); } while (0);
#else
#define LOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0);
#define UNLOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0);
#endif
#ifdef MICROTCP_USING_MUX
typedef struct mux_entry_t mux_entry_t; typedef struct mux_entry_t mux_entry_t;
struct mux_entry_t { struct mux_entry_t {
mux_entry_t **mux_prev; mux_entry_t **mux_prev;
@@ -53,16 +37,13 @@ struct mux_entry_t {
struct microtcp_mux_t { struct microtcp_mux_t {
microtcp_t *mtcp; microtcp_t *mtcp;
#ifdef MICROTCP_BACKGROUND_THREAD
cnd_t queue_not_empty; cnd_t queue_not_empty;
#endif
mux_entry_t *free_list; mux_entry_t *free_list;
mux_entry_t *idle_list; mux_entry_t *idle_list;
mux_entry_t *ready_queue_head; mux_entry_t *ready_queue_head;
mux_entry_t *ready_queue_tail; mux_entry_t *ready_queue_tail;
mux_entry_t entries[MICROTCP_MAX_MUX_ENTRIES]; mux_entry_t entries[MICROTCP_MAX_MUX_ENTRIES];
}; };
#endif
typedef struct buffer_t buffer_t; typedef struct buffer_t buffer_t;
struct buffer_t { struct buffer_t {
@@ -88,7 +69,6 @@ struct microtcp_socket_t {
tcp_connection_t *connection; tcp_connection_t *connection;
}; };
#ifdef MICROTCP_BACKGROUND_THREAD
union { union {
cnd_t something_to_accept; cnd_t something_to_accept;
struct { struct {
@@ -96,22 +76,17 @@ struct microtcp_socket_t {
cnd_t something_to_send; cnd_t something_to_send;
}; };
}; };
#endif
#ifdef MICROTCP_USING_MUX
mux_entry_t *mux_list; mux_entry_t *mux_list;
#endif
}; };
struct microtcp_t { struct microtcp_t {
uint64_t last_update_time_ms; uint64_t last_update_time_ms;
#ifdef MICROTCP_BACKGROUND_THREAD
bool thread_should_stop; bool thread_should_stop;
thrd_t thread_id; thrd_t thread_id;
mtx_t lock; mtx_t lock;
#endif
microtcp_callbacks_t callbacks; microtcp_callbacks_t callbacks;
@@ -393,9 +368,9 @@ process_packet(microtcp_t *mtcp, const void *packet, size_t len)
void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len) void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len)
{ {
LOCK_WHEN_THREADED(mtcp); mtx_lock(&mtcp->lock);
process_packet(mtcp, packet, len); process_packet(mtcp, packet, len);
UNLOCK_WHEN_THREADED(mtcp); mtx_unlock(&mtcp->lock);
} }
static uint64_t get_time_in_ms(void) static uint64_t get_time_in_ms(void)
@@ -419,7 +394,7 @@ void microtcp_step(microtcp_t *mtcp)
uint64_t current_time_ms = get_time_in_ms(); uint64_t current_time_ms = get_time_in_ms();
LOCK_WHEN_THREADED(mtcp); mtx_lock(&mtcp->lock);
{ {
process_packet(mtcp, packet, size); process_packet(mtcp, packet, size);
@@ -432,10 +407,9 @@ void microtcp_step(microtcp_t *mtcp)
mtcp->last_update_time_ms = current_time_ms; mtcp->last_update_time_ms = current_time_ms;
} }
} }
UNLOCK_WHEN_THREADED(mtcp); mtx_unlock(&mtcp->lock);
} }
#ifdef MICROTCP_BACKGROUND_THREAD
static int loop(void *data) static int loop(void *data)
{ {
microtcp_t *mtcp = data; microtcp_t *mtcp = data;
@@ -443,7 +417,6 @@ static int loop(void *data)
microtcp_step(mtcp); microtcp_step(mtcp);
return 0; return 0;
} }
#endif
microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac,
microtcp_callbacks_t callbacks) microtcp_callbacks_t callbacks)
@@ -492,7 +465,7 @@ microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac,
mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].mtcp = NULL; mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].mtcp = NULL;
mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].prev = NULL; mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].prev = NULL;
mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].next = NULL; mtcp->socket_pool[MICROTCP_MAX_SOCKETS-1].next = NULL;
ip_init(&mtcp->ip_state, parsed_ip, mtcp, send_ip_packet); ip_init(&mtcp->ip_state, parsed_ip, mtcp, send_ip_packet);
if (!ip_plug_protocol(&mtcp->ip_state, IP_PROTOCOL_TCP, &mtcp->tcp_state, tcp_process_segment_wrapper)) { if (!ip_plug_protocol(&mtcp->ip_state, IP_PROTOCOL_TCP, &mtcp->tcp_state, tcp_process_segment_wrapper)) {
free(mtcp); free(mtcp);
@@ -508,9 +481,8 @@ microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac,
use_a_buffer(mtcp); use_a_buffer(mtcp);
#ifdef MICROTCP_BACKGROUND_THREAD
{ {
if (mtx_init(&mtcp->lock, mtx_plain) != thrd_success) { if (mtx_init(&mtcp->lock, mtx_recursive) != thrd_success) {
ip_free(&mtcp->ip_state); ip_free(&mtcp->ip_state);
arp_free(&mtcp->arp_state); arp_free(&mtcp->arp_state);
tcp_free(&mtcp->tcp_state); tcp_free(&mtcp->tcp_state);
@@ -527,7 +499,6 @@ microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac,
return NULL; return NULL;
} }
} }
#endif
MICROTCP_DEBUG_LOG("Instanciated (" MICROTCP_DEBUG_LOG("Instanciated ("
"debug=" "debug="
@@ -535,21 +506,12 @@ microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac,
"yes" "yes"
#else #else
"no" "no"
#endif
", thread="
#ifdef MICROTCP_BACKGROUND_THREAD
"yes"
#else
"no"
#endif #endif
")"); ")");
return mtcp; return mtcp;
} }
#ifdef MICROTCP_USING_TAP
static void log_callback_for_tuntap_library(int level, const char *errmsg) static void log_callback_for_tuntap_library(int level, const char *errmsg)
{ {
const char *name; const char *name;
@@ -622,17 +584,14 @@ microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip,
callbacks.free(callbacks.data); callbacks.free(callbacks.data);
return mtcp; return mtcp;
} }
#endif
void microtcp_destroy(microtcp_t *mtcp) void microtcp_destroy(microtcp_t *mtcp)
{ {
#ifdef MICROTCP_BACKGROUND_THREAD
MICROTCP_DEBUG_LOG("Stopping thread"); MICROTCP_DEBUG_LOG("Stopping thread");
mtcp->thread_should_stop = true; mtcp->thread_should_stop = true;
thrd_join(mtcp->thread_id, NULL); thrd_join(mtcp->thread_id, NULL);
mtx_destroy(&mtcp->lock); mtx_destroy(&mtcp->lock);
MICROTCP_DEBUG_LOG("Thread stopped"); MICROTCP_DEBUG_LOG("Thread stopped");
#endif
ip_free(&mtcp->ip_state); ip_free(&mtcp->ip_state);
arp_free(&mtcp->arp_state); arp_free(&mtcp->arp_state);
@@ -687,30 +646,23 @@ push_unlinked_socket_into_free_list(microtcp_t *mtcp, microtcp_socket_t *socket)
mtcp->free_socket_list = socket; mtcp->free_socket_list = socket;
} }
#ifdef MICROTCP_USING_MUX
static void static void
signal_events_to_muxes_associated_to_socket(microtcp_socket_t *socket, int events); signal_events_to_muxes_associated_to_socket(microtcp_socket_t *socket, int events);
#endif
static void listener_event_callback(void *data, tcp_listenevent_t event) static void listener_event_callback(void *data, tcp_listenevent_t event)
{ {
microtcp_socket_t *socket = data; microtcp_socket_t *socket = data;
(void) socket;
#ifdef MICROTCP_BACKGROUND_THREAD
switch (event) {
case TCP_LISTENEVENT_ACCEPT: cnd_signal(&socket->something_to_accept); break;
}
#endif
#ifdef MICROTCP_USING_MUX
int flags = 0; int flags = 0;
switch (event) { switch (event) {
case TCP_LISTENEVENT_ACCEPT: flags = MICROTCP_MUX_ACCEPT; MICROTCP_DEBUG_LOG("Signaling ACCEPT to muxes"); break; case TCP_LISTENEVENT_ACCEPT:
cnd_signal(&socket->something_to_accept);
flags = MICROTCP_MUX_ACCEPT;
MICROTCP_DEBUG_LOG("Signaling ACCEPT to muxes");
break;
} }
if (flags) if (flags)
signal_events_to_muxes_associated_to_socket(socket, flags); signal_events_to_muxes_associated_to_socket(socket, flags);
#endif
} }
microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port, microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port,
@@ -718,7 +670,7 @@ microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port,
{ {
microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE;
microtcp_socket_t *socket = NULL; microtcp_socket_t *socket = NULL;
LOCK_WHEN_THREADED(mtcp); mtx_lock(&mtcp->lock);
{ {
socket = pop_socket_struct_from_free_list(mtcp); socket = pop_socket_struct_from_free_list(mtcp);
if (!socket) { if (!socket) {
@@ -740,23 +692,19 @@ microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port,
socket->next = NULL; socket->next = NULL;
socket->type = SOCKET_LISTENER; socket->type = SOCKET_LISTENER;
socket->listener = listener; socket->listener = listener;
#ifdef MICROTCP_USING_MUX
socket->mux_list = NULL; socket->mux_list = NULL;
#endif
#ifdef MICROTCP_BACKGROUND_THREAD
if (cnd_init(&socket->something_to_accept) != thrd_success) { if (cnd_init(&socket->something_to_accept) != thrd_success) {
errcode2 = MICROTCP_ERRCODE_BADCONDVAR; errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
push_unlinked_socket_into_free_list(mtcp, socket); push_unlinked_socket_into_free_list(mtcp, socket);
tcp_listener_destroy(listener); tcp_listener_destroy(listener);
goto unlock_and_exit; goto unlock_and_exit;
} }
#endif
push_unlinked_socket_into_used_list(socket); push_unlinked_socket_into_used_list(socket);
} }
unlock_and_exit: unlock_and_exit:
UNLOCK_WHEN_THREADED(mtcp); mtx_unlock(&mtcp->lock);
if (errcode) if (errcode)
*errcode = errcode2; *errcode = errcode2;
return socket; return socket;
@@ -769,10 +717,8 @@ void microtcp_close(microtcp_socket_t *socket)
microtcp_t *mtcp = socket->mtcp; microtcp_t *mtcp = socket->mtcp;
LOCK_WHEN_THREADED(mtcp); mtx_lock(&mtcp->lock);
{ {
#ifdef MICROTCP_USING_MUX
// Unregister from all multiplexers // Unregister from all multiplexers
while (socket->mux_list) { while (socket->mux_list) {
// The unregister operation only has // The unregister operation only has
@@ -783,14 +729,11 @@ void microtcp_close(microtcp_socket_t *socket)
socket->mux_list->triggered_events = 0; socket->mux_list->triggered_events = 0;
microtcp_mux_unregister(socket->mux_list->mux, socket, ~0); microtcp_mux_unregister(socket->mux_list->mux, socket, ~0);
} }
#endif
switch (socket->type) { switch (socket->type) {
case SOCKET_LISTENER: case SOCKET_LISTENER:
#ifdef MICROTCP_BACKGROUND_THREAD
cnd_destroy(&socket->something_to_accept); cnd_destroy(&socket->something_to_accept);
#endif
tcp_listener_destroy(socket->listener); tcp_listener_destroy(socket->listener);
break; break;
@@ -804,38 +747,36 @@ void microtcp_close(microtcp_socket_t *socket)
unlink_socket_from_used_socket_list(socket); unlink_socket_from_used_socket_list(socket);
push_unlinked_socket_into_free_list(mtcp, socket); push_unlinked_socket_into_free_list(mtcp, socket);
} }
UNLOCK_WHEN_THREADED(mtcp); mtx_unlock(&mtcp->lock);
} }
static void conn_event_callback(void *data, tcp_connevent_t event) static void conn_event_callback(void *data, tcp_connevent_t event)
{ {
microtcp_socket_t *socket = data; microtcp_socket_t *socket = data;
(void) socket;
#ifdef MICROTCP_BACKGROUND_THREAD int flags;
switch (event) { switch (event) {
case TCP_CONNEVENT_RECV: MICROTCP_DEBUG_LOG("Signal RECV"); cnd_signal(&socket->something_to_recv); break; case TCP_CONNEVENT_RECV:
case TCP_CONNEVENT_SEND: MICROTCP_DEBUG_LOG("Signal SEND"); cnd_signal(&socket->something_to_send); break; MICROTCP_DEBUG_LOG("Signal RECV");
cnd_signal(&socket->something_to_recv);
flags = MICROTCP_MUX_RECV;
break;
case TCP_CONNEVENT_SEND:
MICROTCP_DEBUG_LOG("Signal SEND");
cnd_signal(&socket->something_to_send);
flags = MICROTCP_MUX_SEND;
break;
case TCP_CONNEVENT_RESET: case TCP_CONNEVENT_RESET:
case TCP_CONNEVENT_CLOSE: case TCP_CONNEVENT_CLOSE:
socket->connection = NULL; socket->connection = NULL;
flags = 0; // TODO: Maybe signal closing and reset events to muxes?
break; break;
} }
#endif
#ifdef MICROTCP_USING_MUX
// TODO: Maybe signal closing and reset events to muxes?
int flags;
switch (event) {
case TCP_CONNEVENT_RECV: flags = MICROTCP_MUX_RECV; MICROTCP_DEBUG_LOG("Signaling RECV to muxes"); break;
case TCP_CONNEVENT_SEND: flags = MICROTCP_MUX_SEND; MICROTCP_DEBUG_LOG("Signaling SEND to muxes"); break;
default: flags = 0; break;
}
if (flags) if (flags)
signal_events_to_muxes_associated_to_socket(socket, flags); signal_events_to_muxes_associated_to_socket(socket, flags);
#endif
} }
microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket, microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
@@ -846,7 +787,7 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
microtcp_t *mtcp = socket->mtcp; microtcp_t *mtcp = socket->mtcp;
microtcp_socket_t *socket2 = NULL; microtcp_socket_t *socket2 = NULL;
LOCK_WHEN_THREADED(mtcp); mtx_lock(&mtcp->lock);
{ {
if (socket->type != SOCKET_LISTENER) { if (socket->type != SOCKET_LISTENER) {
errcode2 = MICROTCP_ERRCODE_NOTLISTENER; errcode2 = MICROTCP_ERRCODE_NOTLISTENER;
@@ -861,7 +802,6 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
tcp_connection_t *connection = tcp_listener_accept(socket->listener, socket2, conn_event_callback); tcp_connection_t *connection = tcp_listener_accept(socket->listener, socket2, conn_event_callback);
#ifdef MICROTCP_BACKGROUND_THREAD
while (!connection && !no_block) { while (!connection && !no_block) {
if (cnd_wait(&socket->something_to_accept, &mtcp->lock) != thrd_success) { if (cnd_wait(&socket->something_to_accept, &mtcp->lock) != thrd_success) {
errcode2 = MICROTCP_ERRCODE_BADCONDVAR; errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
@@ -870,28 +810,14 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
} }
connection = tcp_listener_accept(socket->listener, socket2, conn_event_callback); connection = tcp_listener_accept(socket->listener, socket2, conn_event_callback);
} }
#else
if (!connection) {
if (no_block)
errcode2 = MICROTCP_ERRCODE_WOULDBLOCK;
else
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
push_unlinked_socket_into_free_list(mtcp, socket2);
goto unlock_and_exit;
}
#endif
socket2->mtcp = mtcp; socket2->mtcp = mtcp;
socket2->prev = NULL; socket2->prev = NULL;
socket2->next = NULL; socket2->next = NULL;
socket2->type = SOCKET_CONNECTION; socket2->type = SOCKET_CONNECTION;
socket2->connection = connection; socket2->connection = connection;
#ifdef MICROTCP_USING_MUX
socket2->mux_list = NULL; socket2->mux_list = NULL;
#endif
#ifdef MICROTCP_BACKGROUND_THREAD
if (cnd_init(&socket2->something_to_recv) != thrd_success) { if (cnd_init(&socket2->something_to_recv) != thrd_success) {
errcode2 = MICROTCP_ERRCODE_BADCONDVAR; errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
push_unlinked_socket_into_free_list(mtcp, socket2); push_unlinked_socket_into_free_list(mtcp, socket2);
@@ -903,13 +829,12 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
push_unlinked_socket_into_free_list(mtcp, socket2); push_unlinked_socket_into_free_list(mtcp, socket2);
goto unlock_and_exit; goto unlock_and_exit;
} }
#endif
push_unlinked_socket_into_used_list(socket2); push_unlinked_socket_into_used_list(socket2);
} }
unlock_and_exit: unlock_and_exit:
UNLOCK_WHEN_THREADED(mtcp); mtx_unlock(&mtcp->lock);
if (errcode) if (errcode)
*errcode = errcode2; *errcode = errcode2;
@@ -938,11 +863,10 @@ size_t microtcp_recv(microtcp_socket_t *socket,
microtcp_t *mtcp = socket->mtcp; microtcp_t *mtcp = socket->mtcp;
microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE;
LOCK_WHEN_THREADED(mtcp); mtx_lock(&mtcp->lock);
{ {
num = tcp_connection_recv(socket->connection, dst, len); num = tcp_connection_recv(socket->connection, dst, len);
#ifdef MICROTCP_BACKGROUND_THREAD
while (num == 0 && !no_block) { while (num == 0 && !no_block) {
if (cnd_wait(&socket->something_to_recv, &mtcp->lock) != thrd_success) { if (cnd_wait(&socket->something_to_recv, &mtcp->lock) != thrd_success) {
errcode2 = MICROTCP_ERRCODE_BADCONDVAR; errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
@@ -950,7 +874,7 @@ size_t microtcp_recv(microtcp_socket_t *socket,
} }
num = tcp_connection_recv(socket->connection, dst, len); num = tcp_connection_recv(socket->connection, dst, len);
} }
#endif
if (num == 0) { if (num == 0) {
if (no_block) if (no_block)
errcode2 = MICROTCP_ERRCODE_WOULDBLOCK; errcode2 = MICROTCP_ERRCODE_WOULDBLOCK;
@@ -962,7 +886,7 @@ size_t microtcp_recv(microtcp_socket_t *socket,
goto unlock_and_exit; // Warning goto unlock_and_exit; // Warning
unlock_and_exit: unlock_and_exit:
UNLOCK_WHEN_THREADED(mtcp); mtx_unlock(&mtcp->lock);
if (errcode) if (errcode)
*errcode = errcode2; *errcode = errcode2;
@@ -990,11 +914,10 @@ size_t microtcp_send(microtcp_socket_t *socket,
microtcp_t *mtcp = socket->mtcp; microtcp_t *mtcp = socket->mtcp;
microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE; microtcp_errcode_t errcode2 = MICROTCP_ERRCODE_NONE;
LOCK_WHEN_THREADED(mtcp); mtx_lock(&mtcp->lock);
{ {
num = tcp_connection_send(socket->connection, src, len); num = tcp_connection_send(socket->connection, src, len);
#ifdef MICROTCP_BACKGROUND_THREAD
while (num == 0 && !no_block) { while (num == 0 && !no_block) {
if (cnd_wait(&socket->something_to_send, &mtcp->lock) != thrd_success) { if (cnd_wait(&socket->something_to_send, &mtcp->lock) != thrd_success) {
errcode2 = MICROTCP_ERRCODE_BADCONDVAR; errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
@@ -1002,7 +925,7 @@ size_t microtcp_send(microtcp_socket_t *socket,
} }
num = tcp_connection_send(socket->connection, src, len); num = tcp_connection_send(socket->connection, src, len);
} }
#endif
if (num == 0) { if (num == 0) {
if (no_block) if (no_block)
errcode2 = MICROTCP_ERRCODE_WOULDBLOCK; errcode2 = MICROTCP_ERRCODE_WOULDBLOCK;
@@ -1012,14 +935,13 @@ size_t microtcp_send(microtcp_socket_t *socket,
} }
goto unlock_and_exit; // Warning goto unlock_and_exit; // Warning
unlock_and_exit: unlock_and_exit:
UNLOCK_WHEN_THREADED(mtcp); mtx_unlock(&mtcp->lock);
if (errcode) if (errcode)
*errcode = errcode2; *errcode = errcode2;
return num; return num;
} }
#ifdef MICROTCP_USING_MUX
microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp) microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp)
{ {
microtcp_mux_t *mux = malloc(sizeof(microtcp_mux_t)); microtcp_mux_t *mux = malloc(sizeof(microtcp_mux_t));
@@ -1048,12 +970,10 @@ microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp)
mux->ready_queue_head = NULL; mux->ready_queue_head = NULL;
mux->ready_queue_tail = NULL; mux->ready_queue_tail = NULL;
#ifdef MICROTCP_BACKGROUND_THREAD
if (cnd_init(&mux->queue_not_empty) != thrd_success) { if (cnd_init(&mux->queue_not_empty) != thrd_success) {
free(mux); free(mux);
return NULL; return NULL;
} }
#endif
return mux; return mux;
} }
@@ -1084,10 +1004,7 @@ void microtcp_mux_destroy(microtcp_mux_t *mux)
assert(entry != mux->ready_queue_head); assert(entry != mux->ready_queue_head);
} }
#ifdef MICROTCP_BACKGROUND_THREAD
cnd_destroy(&mux->queue_not_empty); cnd_destroy(&mux->queue_not_empty);
#endif
free(mux); free(mux);
} }
@@ -1152,7 +1069,7 @@ move_mux_entry_to_idle_list(mux_entry_t *entry)
bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events) bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events)
{ {
LOCK_WHEN_THREADED(mux->mtcp); mtx_lock(&mux->mtcp->lock);
// There's no need to check that mux // There's no need to check that mux
// and socket have the same mtcp because // and socket have the same mtcp because
@@ -1163,7 +1080,7 @@ bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int e
mux_entry_t *entry = find_socket_and_mux_entry(mux, sock); mux_entry_t *entry = find_socket_and_mux_entry(mux, sock);
if (!entry) { if (!entry) {
// This socket wasn't registered into the mux // This socket wasn't registered into the mux
UNLOCK_WHEN_THREADED(mux->mtcp); mtx_unlock(&mux->mtcp->lock);
return false; return false;
} }
@@ -1184,21 +1101,21 @@ bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int e
// move the entry to the free list. // move the entry to the free list.
move_mux_entry_to_free_list(entry); move_mux_entry_to_free_list(entry);
UNLOCK_WHEN_THREADED(mux->mtcp); mtx_unlock(&mux->mtcp->lock);
return true; return true;
} }
bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp) bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp)
{ {
LOCK_WHEN_THREADED(mux->mtcp); mtx_lock(&mux->mtcp->lock);
if (mux->mtcp != sock->mtcp) { if (mux->mtcp != sock->mtcp) {
UNLOCK_WHEN_THREADED(mux->mtcp); mtx_unlock(&mux->mtcp->lock);
return false; // mux and socket are associated to different microtcp stacks return false; // mux and socket are associated to different microtcp stacks
} }
if (events == 0) { if (events == 0) {
UNLOCK_WHEN_THREADED(mux->mtcp); mtx_unlock(&mux->mtcp->lock);
return true; // Nothing to be done return true; // Nothing to be done
} }
@@ -1209,7 +1126,7 @@ bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int eve
if (mux->free_list == NULL) { if (mux->free_list == NULL) {
// The entry limit was reached. // The entry limit was reached.
// It's impossible to register the socket at this time // It's impossible to register the socket at this time
UNLOCK_WHEN_THREADED(mux->mtcp); mtx_unlock(&mux->mtcp->lock);
return false; return false;
} }
@@ -1243,7 +1160,7 @@ bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int eve
entry->events_of_interest |= events; entry->events_of_interest |= events;
UNLOCK_WHEN_THREADED(mux->mtcp); mtx_unlock(&mux->mtcp->lock);
return true; return true;
} }
@@ -1284,19 +1201,15 @@ static bool mux_poll(microtcp_mux_t *mux, microtcp_muxevent_t *ev)
bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev) bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev)
{ {
#ifdef MICROTCP_BACKGROUND_THREAD mtx_lock(&mux->mtcp->lock);
LOCK_WHEN_THREADED(mux->mtcp);
while (!mux_poll(mux, ev)) { while (!mux_poll(mux, ev)) {
MICROTCP_DEBUG_LOG("Multiplexer waiting for an event"); MICROTCP_DEBUG_LOG("Multiplexer waiting for an event");
if (cnd_wait(&mux->queue_not_empty, &mux->mtcp->lock) != thrd_success) if (cnd_wait(&mux->queue_not_empty, &mux->mtcp->lock) != thrd_success)
abort(); abort();
MICROTCP_DEBUG_LOG("Multiplexer woke up for an event"); MICROTCP_DEBUG_LOG("Multiplexer woke up for an event");
} }
UNLOCK_WHEN_THREADED(mux->mtcp); mtx_unlock(&mux->mtcp->lock);
return true; return true;
#else
return mux_poll(mux, ev);
#endif
} }
static void static void
@@ -1350,17 +1263,12 @@ signal_events_to_muxes_associated_to_socket(microtcp_socket_t *socket, int event
entry->mux_next = NULL; entry->mux_next = NULL;
mux->ready_queue_tail = entry; mux->ready_queue_tail = entry;
#ifdef MICROTCP_BACKGROUND_THREAD
MICROTCP_DEBUG_LOG("Signaling event to multiplexer"); MICROTCP_DEBUG_LOG("Signaling event to multiplexer");
if (queue_was_empty) if (queue_was_empty)
cnd_signal(&mux->queue_not_empty); cnd_signal(&mux->queue_not_empty);
MICROTCP_DEBUG_LOG("Signaled event to multiplexer"); MICROTCP_DEBUG_LOG("Signaled event to multiplexer");
#else
(void) queue_was_empty;
#endif
} }
entry = entry->sock_next; entry = entry->sock_next;
} }
MICROTCP_DEBUG_LOG("Socket signaled to multiplexers"); MICROTCP_DEBUG_LOG("Socket signaled to multiplexers");
} }
#endif
+3 -3
View File
@@ -1,3 +1,5 @@
#ifndef MICROTCP_H
#define MICROTCP_H
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@@ -44,10 +46,8 @@ typedef struct {
int (*recv)(void *data, void *dst, size_t len); int (*recv)(void *data, void *dst, size_t len);
} microtcp_callbacks_t; } microtcp_callbacks_t;
#ifdef MICROTCP_USING_TAP
bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac, microtcp_callbacks_t *callbacks); bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac, microtcp_callbacks_t *callbacks);
microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip, const char *tap_mac, const char *stack_mac); microtcp_t *microtcp_create(const char *tap_ip, const char *stack_ip, const char *tap_mac, const char *stack_mac);
#endif
microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_callbacks_t callbacks); microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_callbacks_t callbacks);
void microtcp_destroy(microtcp_t *mtcp); void microtcp_destroy(microtcp_t *mtcp);
@@ -60,7 +60,6 @@ size_t microtcp_recv(microtcp_socket_t *socket, void *dst, siz
void microtcp_step(microtcp_t *mtcp); void microtcp_step(microtcp_t *mtcp);
void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len); void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len);
#ifdef MICROTCP_USING_MUX
typedef enum { typedef enum {
MICROTCP_MUX_ACCEPT = 1, MICROTCP_MUX_ACCEPT = 1,
MICROTCP_MUX_RECV = 2, MICROTCP_MUX_RECV = 2,
@@ -79,4 +78,5 @@ void microtcp_mux_destroy(microtcp_mux_t *mux);
bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp); bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp);
bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events); bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events);
bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev); bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev);
#endif #endif
+4 -7
View File
@@ -1,11 +1,8 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdalign.h> #include <stdalign.h>
#include "endian.h"
#ifndef MICROTCP_AMALGAMATION #include "tcp.h"
# include "endian.h"
# include "tcp.h"
#endif
#ifdef TCP_DEBUG #ifdef TCP_DEBUG
# include <stdio.h> # include <stdio.h>
@@ -444,8 +441,8 @@ static void retransmit(tcp_connection_t *c)
size_t retr_queue_bytes = c->snd_nxt - c->snd_una; size_t retr_queue_bytes = c->snd_nxt - c->snd_una;
assert(retr_queue_bytes > 0); // If there were no bytes to ACK, //assert(retr_queue_bytes > 0); // If there were no bytes to ACK,
// there would be no active timer. // there would be no active timer.
size_t retr_queue_ghost = 0; size_t retr_queue_ghost = 0;
if (c->waiting_ack_for_syn) retr_queue_ghost++; if (c->waiting_ack_for_syn) retr_queue_ghost++;
-3
View File
@@ -1,10 +1,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#ifndef MICROTCP_AMALGAMATION
#include "defs.h" #include "defs.h"
#include "tcp_timer.h" #include "tcp_timer.h"
#endif
#define TCP_TIMEOUT_TIME_WAIT 1000 #define TCP_TIMEOUT_TIME_WAIT 1000
//240000 //240000
-3
View File
@@ -1,9 +1,6 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#ifndef MICROTCP_AMALGAMATION
#include "tcp_timer.h" #include "tcp_timer.h"
#endif
void tcp_timerset_init(tcp_timerset_t *set) void tcp_timerset_init(tcp_timerset_t *set)
{ {
+931
View File
@@ -0,0 +1,931 @@
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2012 Marcus Geelnard
Copyright (c) 2013-2016 Evan Nemerson
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "tinycthread.h"
#include <stdlib.h>
/* Platform specific includes */
#if defined(_TTHREAD_POSIX_)
#include <signal.h>
#include <sched.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#elif defined(_TTHREAD_WIN32_)
#include <process.h>
#include <sys/timeb.h>
#endif
/* Standard, good-to-have defines */
#ifndef NULL
#define NULL (void*)0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
int mtx_init(mtx_t *mtx, int type)
{
#if defined(_TTHREAD_WIN32_)
mtx->mAlreadyLocked = FALSE;
mtx->mRecursive = type & mtx_recursive;
mtx->mTimed = type & mtx_timed;
if (!mtx->mTimed)
{
InitializeCriticalSection(&(mtx->mHandle.cs));
}
else
{
mtx->mHandle.mut = CreateMutex(NULL, FALSE, NULL);
if (mtx->mHandle.mut == NULL)
{
return thrd_error;
}
}
return thrd_success;
#else
int ret;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
if (type & mtx_recursive)
{
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
}
ret = pthread_mutex_init(mtx, &attr);
pthread_mutexattr_destroy(&attr);
return ret == 0 ? thrd_success : thrd_error;
#endif
}
void mtx_destroy(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
if (!mtx->mTimed)
{
DeleteCriticalSection(&(mtx->mHandle.cs));
}
else
{
CloseHandle(mtx->mHandle.mut);
}
#else
pthread_mutex_destroy(mtx);
#endif
}
int mtx_lock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
if (!mtx->mTimed)
{
EnterCriticalSection(&(mtx->mHandle.cs));
}
else
{
switch (WaitForSingleObject(mtx->mHandle.mut, INFINITE))
{
case WAIT_OBJECT_0:
break;
case WAIT_ABANDONED:
default:
return thrd_error;
}
}
if (!mtx->mRecursive)
{
while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
mtx->mAlreadyLocked = TRUE;
}
return thrd_success;
#else
return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
#endif
}
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
{
#if defined(_TTHREAD_WIN32_)
struct timespec current_ts;
DWORD timeoutMs;
if (!mtx->mTimed)
{
return thrd_error;
}
timespec_get(&current_ts, TIME_UTC);
if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec)))
{
timeoutMs = 0;
}
else
{
timeoutMs = (DWORD)(ts->tv_sec - current_ts.tv_sec) * 1000;
timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000;
timeoutMs += 1;
}
/* TODO: the timeout for WaitForSingleObject doesn't include time
while the computer is asleep. */
switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs))
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
return thrd_timedout;
case WAIT_ABANDONED:
default:
return thrd_error;
}
if (!mtx->mRecursive)
{
while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
mtx->mAlreadyLocked = TRUE;
}
return thrd_success;
#elif defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L) && defined(_POSIX_THREADS) && (_POSIX_THREADS >= 200112L)
switch (pthread_mutex_timedlock(mtx, ts)) {
case 0:
return thrd_success;
case ETIMEDOUT:
return thrd_timedout;
default:
return thrd_error;
}
#else
int rc;
struct timespec cur, dur;
/* Try to acquire the lock and, if we fail, sleep for 5ms. */
while ((rc = pthread_mutex_trylock (mtx)) == EBUSY) {
timespec_get(&cur, TIME_UTC);
if ((cur.tv_sec > ts->tv_sec) || ((cur.tv_sec == ts->tv_sec) && (cur.tv_nsec >= ts->tv_nsec)))
{
break;
}
dur.tv_sec = ts->tv_sec - cur.tv_sec;
dur.tv_nsec = ts->tv_nsec - cur.tv_nsec;
if (dur.tv_nsec < 0)
{
dur.tv_sec--;
dur.tv_nsec += 1000000000;
}
if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000))
{
dur.tv_sec = 0;
dur.tv_nsec = 5000000;
}
nanosleep(&dur, NULL);
}
switch (rc) {
case 0:
return thrd_success;
case ETIMEDOUT:
case EBUSY:
return thrd_timedout;
default:
return thrd_error;
}
#endif
}
int mtx_trylock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
int ret;
if (!mtx->mTimed)
{
ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? thrd_success : thrd_busy;
}
else
{
ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? thrd_success : thrd_busy;
}
if ((!mtx->mRecursive) && (ret == thrd_success))
{
if (mtx->mAlreadyLocked)
{
LeaveCriticalSection(&(mtx->mHandle.cs));
ret = thrd_busy;
}
else
{
mtx->mAlreadyLocked = TRUE;
}
}
return ret;
#else
return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
#endif
}
int mtx_unlock(mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
mtx->mAlreadyLocked = FALSE;
if (!mtx->mTimed)
{
LeaveCriticalSection(&(mtx->mHandle.cs));
}
else
{
if (!ReleaseMutex(mtx->mHandle.mut))
{
return thrd_error;
}
}
return thrd_success;
#else
return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
#endif
}
#if defined(_TTHREAD_WIN32_)
#define _CONDITION_EVENT_ONE 0
#define _CONDITION_EVENT_ALL 1
#endif
int cnd_init(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
cond->mWaitersCount = 0;
/* Init critical section */
InitializeCriticalSection(&cond->mWaitersCountLock);
/* Init events */
cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
{
cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
return thrd_error;
}
cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
return thrd_error;
}
return thrd_success;
#else
return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
#endif
}
void cnd_destroy(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
}
if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
{
CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
}
DeleteCriticalSection(&cond->mWaitersCountLock);
#else
pthread_cond_destroy(cond);
#endif
}
int cnd_signal(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
int haveWaiters;
/* Are there any waiters? */
EnterCriticalSection(&cond->mWaitersCountLock);
haveWaiters = (cond->mWaitersCount > 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we have any waiting threads, send them a signal */
if(haveWaiters)
{
if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
{
return thrd_error;
}
}
return thrd_success;
#else
return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
#endif
}
int cnd_broadcast(cnd_t *cond)
{
#if defined(_TTHREAD_WIN32_)
int haveWaiters;
/* Are there any waiters? */
EnterCriticalSection(&cond->mWaitersCountLock);
haveWaiters = (cond->mWaitersCount > 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we have any waiting threads, send them a signal */
if(haveWaiters)
{
if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
{
return thrd_error;
}
}
return thrd_success;
#else
return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error;
#endif
}
#if defined(_TTHREAD_WIN32_)
static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
{
DWORD result;
int lastWaiter;
/* Increment number of waiters */
EnterCriticalSection(&cond->mWaitersCountLock);
++ cond->mWaitersCount;
LeaveCriticalSection(&cond->mWaitersCountLock);
/* Release the mutex while waiting for the condition (will decrease
the number of waiters when done)... */
mtx_unlock(mtx);
/* Wait for either event to become signaled due to cnd_signal() or
cnd_broadcast() being called */
result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
if (result == WAIT_TIMEOUT)
{
/* The mutex is locked again before the function returns, even if an error occurred */
mtx_lock(mtx);
return thrd_timedout;
}
else if (result == WAIT_FAILED)
{
/* The mutex is locked again before the function returns, even if an error occurred */
mtx_lock(mtx);
return thrd_error;
}
/* Check if we are the last waiter */
EnterCriticalSection(&cond->mWaitersCountLock);
-- cond->mWaitersCount;
lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
(cond->mWaitersCount == 0);
LeaveCriticalSection(&cond->mWaitersCountLock);
/* If we are the last waiter to be notified to stop waiting, reset the event */
if (lastWaiter)
{
if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
{
/* The mutex is locked again before the function returns, even if an error occurred */
mtx_lock(mtx);
return thrd_error;
}
}
/* Re-acquire the mutex */
mtx_lock(mtx);
return thrd_success;
}
#endif
int cnd_wait(cnd_t *cond, mtx_t *mtx)
{
#if defined(_TTHREAD_WIN32_)
return _cnd_timedwait_win32(cond, mtx, INFINITE);
#else
return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
#endif
}
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
{
#if defined(_TTHREAD_WIN32_)
struct timespec now;
if (timespec_get(&now, TIME_UTC) == TIME_UTC)
{
unsigned long long nowInMilliseconds = now.tv_sec * 1000 + now.tv_nsec / 1000000;
unsigned long long tsInMilliseconds = ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
DWORD delta = (tsInMilliseconds > nowInMilliseconds) ?
(DWORD)(tsInMilliseconds - nowInMilliseconds) : 0;
return _cnd_timedwait_win32(cond, mtx, delta);
}
else
return thrd_error;
#else
int ret;
ret = pthread_cond_timedwait(cond, mtx, ts);
if (ret == ETIMEDOUT)
{
return thrd_timedout;
}
return ret == 0 ? thrd_success : thrd_error;
#endif
}
#if defined(_TTHREAD_WIN32_)
struct TinyCThreadTSSData {
void* value;
tss_t key;
struct TinyCThreadTSSData* next;
};
static tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, };
static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL;
static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL;
static void _tinycthread_tss_cleanup (void);
static void _tinycthread_tss_cleanup (void) {
struct TinyCThreadTSSData* data;
int iteration;
unsigned int again = 1;
void* value;
for (iteration = 0 ; iteration < TSS_DTOR_ITERATIONS && again > 0 ; iteration++)
{
again = 0;
for (data = _tinycthread_tss_head ; data != NULL ; data = data->next)
{
if (data->value != NULL)
{
value = data->value;
data->value = NULL;
if (_tinycthread_tss_dtors[data->key] != NULL)
{
again = 1;
_tinycthread_tss_dtors[data->key](value);
}
}
}
}
while (_tinycthread_tss_head != NULL) {
data = _tinycthread_tss_head->next;
free (_tinycthread_tss_head);
_tinycthread_tss_head = data;
}
_tinycthread_tss_head = NULL;
_tinycthread_tss_tail = NULL;
}
static void NTAPI _tinycthread_tss_callback(PVOID h, DWORD dwReason, PVOID pv)
{
(void)h;
(void)pv;
if (_tinycthread_tss_head != NULL && (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH))
{
_tinycthread_tss_cleanup();
}
}
#if defined(_MSC_VER)
#ifdef _M_X64
#pragma const_seg(".CRT$XLB")
#else
#pragma data_seg(".CRT$XLB")
#endif
PIMAGE_TLS_CALLBACK p_thread_callback = _tinycthread_tss_callback;
#ifdef _M_X64
#pragma data_seg()
#else
#pragma const_seg()
#endif
#else
PIMAGE_TLS_CALLBACK p_thread_callback __attribute__((section(".CRT$XLB"))) = _tinycthread_tss_callback;
#endif
#endif /* defined(_TTHREAD_WIN32_) */
/** Information to pass to the new thread (what to run). */
typedef struct {
thrd_start_t mFunction; /**< Pointer to the function to be executed. */
void * mArg; /**< Function argument for the thread function. */
} _thread_start_info;
/* Thread wrapper function. */
#if defined(_TTHREAD_WIN32_)
static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg)
#elif defined(_TTHREAD_POSIX_)
static void * _thrd_wrapper_function(void * aArg)
#endif
{
thrd_start_t fun;
void *arg;
int res;
/* Get thread startup information */
_thread_start_info *ti = (_thread_start_info *) aArg;
fun = ti->mFunction;
arg = ti->mArg;
/* The thread is responsible for freeing the startup information */
free((void *)ti);
/* Call the actual client thread function */
res = fun(arg);
#if defined(_TTHREAD_WIN32_)
if (_tinycthread_tss_head != NULL)
{
_tinycthread_tss_cleanup();
}
return (DWORD)res;
#else
return (void*)(intptr_t)res;
#endif
}
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
{
/* Fill out the thread startup information (passed to the thread wrapper,
which will eventually free it) */
_thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
if (ti == NULL)
{
return thrd_nomem;
}
ti->mFunction = func;
ti->mArg = arg;
/* Create the thread */
#if defined(_TTHREAD_WIN32_)
*thr = CreateThread(NULL, 0, _thrd_wrapper_function, (LPVOID) ti, 0, NULL);
#elif defined(_TTHREAD_POSIX_)
if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
{
*thr = 0;
}
#endif
/* Did we fail to create the thread? */
if(!*thr)
{
free(ti);
return thrd_error;
}
return thrd_success;
}
thrd_t thrd_current(void)
{
#if defined(_TTHREAD_WIN32_)
return GetCurrentThread();
#else
return pthread_self();
#endif
}
int thrd_detach(thrd_t thr)
{
#if defined(_TTHREAD_WIN32_)
/* https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 */
return CloseHandle(thr) != 0 ? thrd_success : thrd_error;
#else
return pthread_detach(thr) == 0 ? thrd_success : thrd_error;
#endif
}
int thrd_equal(thrd_t thr0, thrd_t thr1)
{
#if defined(_TTHREAD_WIN32_)
return GetThreadId(thr0) == GetThreadId(thr1);
#else
return pthread_equal(thr0, thr1);
#endif
}
void thrd_exit(int res)
{
#if defined(_TTHREAD_WIN32_)
if (_tinycthread_tss_head != NULL)
{
_tinycthread_tss_cleanup();
}
ExitThread((DWORD)res);
#else
pthread_exit((void*)(intptr_t)res);
#endif
}
int thrd_join(thrd_t thr, int *res)
{
#if defined(_TTHREAD_WIN32_)
DWORD dwRes;
if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
{
return thrd_error;
}
if (res != NULL)
{
if (GetExitCodeThread(thr, &dwRes) != 0)
{
*res = (int) dwRes;
}
else
{
return thrd_error;
}
}
CloseHandle(thr);
#elif defined(_TTHREAD_POSIX_)
void *pres;
if (pthread_join(thr, &pres) != 0)
{
return thrd_error;
}
if (res != NULL)
{
*res = (int)(intptr_t)pres;
}
#endif
return thrd_success;
}
int thrd_sleep(const struct timespec *duration, struct timespec *remaining)
{
#if !defined(_TTHREAD_WIN32_)
int res = nanosleep(duration, remaining);
if (res == 0) {
return 0;
} else if (errno == EINTR) {
return -1;
} else {
return -2;
}
#else
struct timespec start;
DWORD t;
timespec_get(&start, TIME_UTC);
t = SleepEx((DWORD)(duration->tv_sec * 1000 +
duration->tv_nsec / 1000000 +
(((duration->tv_nsec % 1000000) == 0) ? 0 : 1)),
TRUE);
if (t == 0) {
return 0;
} else {
if (remaining != NULL) {
timespec_get(remaining, TIME_UTC);
remaining->tv_sec -= start.tv_sec;
remaining->tv_nsec -= start.tv_nsec;
if (remaining->tv_nsec < 0)
{
remaining->tv_nsec += 1000000000;
remaining->tv_sec -= 1;
}
}
return (t == WAIT_IO_COMPLETION) ? -1 : -2;
}
#endif
}
void thrd_yield(void)
{
#if defined(_TTHREAD_WIN32_)
Sleep(0);
#else
sched_yield();
#endif
}
int tss_create(tss_t *key, tss_dtor_t dtor)
{
#if defined(_TTHREAD_WIN32_)
*key = TlsAlloc();
if (*key == TLS_OUT_OF_INDEXES)
{
return thrd_error;
}
_tinycthread_tss_dtors[*key] = dtor;
#else
if (pthread_key_create(key, dtor) != 0)
{
return thrd_error;
}
#endif
return thrd_success;
}
void tss_delete(tss_t key)
{
#if defined(_TTHREAD_WIN32_)
struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*) TlsGetValue (key);
struct TinyCThreadTSSData* prev = NULL;
if (data != NULL)
{
if (data == _tinycthread_tss_head)
{
_tinycthread_tss_head = data->next;
}
else
{
prev = _tinycthread_tss_head;
if (prev != NULL)
{
while (prev->next != data)
{
prev = prev->next;
}
}
}
if (data == _tinycthread_tss_tail)
{
_tinycthread_tss_tail = prev;
}
free (data);
}
_tinycthread_tss_dtors[key] = NULL;
TlsFree(key);
#else
pthread_key_delete(key);
#endif
}
void *tss_get(tss_t key)
{
#if defined(_TTHREAD_WIN32_)
struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
if (data == NULL)
{
return NULL;
}
return data->value;
#else
return pthread_getspecific(key);
#endif
}
int tss_set(tss_t key, void *val)
{
#if defined(_TTHREAD_WIN32_)
struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
if (data == NULL)
{
data = (struct TinyCThreadTSSData*)malloc(sizeof(struct TinyCThreadTSSData));
if (data == NULL)
{
return thrd_error;
}
data->value = NULL;
data->key = key;
data->next = NULL;
if (_tinycthread_tss_tail != NULL)
{
_tinycthread_tss_tail->next = data;
}
else
{
_tinycthread_tss_tail = data;
}
if (_tinycthread_tss_head == NULL)
{
_tinycthread_tss_head = data;
}
if (!TlsSetValue(key, data))
{
free (data);
return thrd_error;
}
}
data->value = val;
#else
if (pthread_setspecific(key, val) != 0)
{
return thrd_error;
}
#endif
return thrd_success;
}
#if defined(_TTHREAD_EMULATE_TIMESPEC_GET_)
int _tthread_timespec_get(struct timespec *ts, int base)
{
#if defined(_TTHREAD_WIN32_)
struct _timeb tb;
#elif !defined(CLOCK_REALTIME)
struct timeval tv;
#endif
if (base != TIME_UTC)
{
return 0;
}
#if defined(_TTHREAD_WIN32_)
_ftime_s(&tb);
ts->tv_sec = (time_t)tb.time;
ts->tv_nsec = 1000000L * (long)tb.millitm;
#elif defined(CLOCK_REALTIME)
base = (clock_gettime(CLOCK_REALTIME, ts) == 0) ? base : 0;
#else
gettimeofday(&tv, NULL);
ts->tv_sec = (time_t)tv.tv_sec;
ts->tv_nsec = 1000L * (long)tv.tv_usec;
#endif
return base;
}
#endif /* _TTHREAD_EMULATE_TIMESPEC_GET_ */
#if defined(_TTHREAD_WIN32_)
void call_once(once_flag *flag, void (*func)(void))
{
/* The idea here is that we use a spin lock (via the
InterlockedCompareExchange function) to restrict access to the
critical section until we have initialized it, then we use the
critical section to block until the callback has completed
execution. */
while (flag->status < 3)
{
switch (flag->status)
{
case 0:
if (InterlockedCompareExchange (&(flag->status), 1, 0) == 0) {
InitializeCriticalSection(&(flag->lock));
EnterCriticalSection(&(flag->lock));
flag->status = 2;
func();
flag->status = 3;
LeaveCriticalSection(&(flag->lock));
return;
}
break;
case 1:
break;
case 2:
EnterCriticalSection(&(flag->lock));
LeaveCriticalSection(&(flag->lock));
break;
}
}
}
#endif /* defined(_TTHREAD_WIN32_) */
#ifdef __cplusplus
}
#endif
+479
View File
@@ -0,0 +1,479 @@
/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2012 Marcus Geelnard
Copyright (c) 2013-2016 Evan Nemerson
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef _TINYCTHREAD_H_
#define _TINYCTHREAD_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file
* @mainpage TinyCThread API Reference
*
* @section intro_sec Introduction
* TinyCThread is a minimal, portable implementation of basic threading
* classes for C.
*
* They closely mimic the functionality and naming of the C11 standard, and
* should be easily replaceable with the corresponding standard variants.
*
* @section port_sec Portability
* The Win32 variant uses the native Win32 API for implementing the thread
* classes, while for other systems, the POSIX threads API (pthread) is used.
*
* @section misc_sec Miscellaneous
* The following special keywords are available: #_Thread_local.
*
* For more detailed information, browse the different sections of this
* documentation. A good place to start is:
* tinycthread.h.
*/
/* Which platform are we on? */
#if !defined(_TTHREAD_PLATFORM_DEFINED_)
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
#define _TTHREAD_WIN32_
#else
#define _TTHREAD_POSIX_
#endif
#define _TTHREAD_PLATFORM_DEFINED_
#endif
/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
#if defined(_TTHREAD_POSIX_)
#undef _FEATURES_H
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
#if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
#define _XPG6
#endif
/* Generic includes */
#include <time.h>
/* Platform specific includes */
#if defined(_TTHREAD_POSIX_)
#include <pthread.h>
#elif defined(_TTHREAD_WIN32_)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define __UNDEF_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifdef __UNDEF_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#undef __UNDEF_LEAN_AND_MEAN
#endif
#endif
/* Compiler-specific information */
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define TTHREAD_NORETURN _Noreturn
#elif defined(__GNUC__)
#define TTHREAD_NORETURN __attribute__((__noreturn__))
#else
#define TTHREAD_NORETURN
#endif
/* If TIME_UTC is missing, provide it and provide a wrapper for
timespec_get. */
#ifndef TIME_UTC
#define TIME_UTC 1
#define _TTHREAD_EMULATE_TIMESPEC_GET_
#if defined(_TTHREAD_WIN32_)
struct _tthread_timespec {
time_t tv_sec;
long tv_nsec;
};
#define timespec _tthread_timespec
#endif
int _tthread_timespec_get(struct timespec *ts, int base);
#define timespec_get _tthread_timespec_get
#endif
/** TinyCThread version (major number). */
#define TINYCTHREAD_VERSION_MAJOR 1
/** TinyCThread version (minor number). */
#define TINYCTHREAD_VERSION_MINOR 2
/** TinyCThread version (full version). */
#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR)
/**
* @def _Thread_local
* Thread local storage keyword.
* A variable that is declared with the @c _Thread_local keyword makes the
* value of the variable local to each thread (known as thread-local storage,
* or TLS). Example usage:
* @code
* // This variable is local to each thread.
* _Thread_local int variable;
* @endcode
* @note The @c _Thread_local keyword is a macro that maps to the corresponding
* compiler directive (e.g. @c __declspec(thread)).
* @note This directive is currently not supported on Mac OS X (it will give
* a compiler error), since compile-time TLS is not supported in the Mac OS X
* executable format. Also, some older versions of MinGW (before GCC 4.x) do
* not support this directive, nor does the Tiny C Compiler.
* @hideinitializer
*/
#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
#define _Thread_local __thread
#else
#define _Thread_local __declspec(thread)
#endif
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9))
#define _Thread_local __thread
#endif
/* Macros */
#if defined(_TTHREAD_WIN32_)
#define TSS_DTOR_ITERATIONS (4)
#else
#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
#endif
/* Function return values */
#define thrd_error 0 /**< The requested operation failed */
#define thrd_success 1 /**< The requested operation succeeded */
#define thrd_timedout 2 /**< The time specified in the call was reached without acquiring the requested resource */
#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */
#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */
/* Mutex types */
#define mtx_plain 0
#define mtx_timed 1
#define mtx_recursive 2
/* Mutex */
#if defined(_TTHREAD_WIN32_)
typedef struct {
union {
CRITICAL_SECTION cs; /* Critical section handle (used for non-timed mutexes) */
HANDLE mut; /* Mutex handle (used for timed mutex) */
} mHandle; /* Mutex handle */
int mAlreadyLocked; /* TRUE if the mutex is already locked */
int mRecursive; /* TRUE if the mutex is recursive */
int mTimed; /* TRUE if the mutex is timed */
} mtx_t;
#else
typedef pthread_mutex_t mtx_t;
#endif
/** Create a mutex object.
* @param mtx A mutex object.
* @param type Bit-mask that must have one of the following six values:
* @li @c mtx_plain for a simple non-recursive mutex
* @li @c mtx_timed for a non-recursive mutex that supports timeout
* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_init(mtx_t *mtx, int type);
/** Release any resources used by the given mutex.
* @param mtx A mutex object.
*/
void mtx_destroy(mtx_t *mtx);
/** Lock the given mutex.
* Blocks until the given mutex can be locked. If the mutex is non-recursive, and
* the calling thread already has a lock on the mutex, this call will block
* forever.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_lock(mtx_t *mtx);
/** Lock the given mutex, or block until a specific point in time.
* Blocks until either the given mutex can be locked, or the specified TIME_UTC
* based time.
* @param mtx A mutex object.
* @param ts A UTC based calendar time
* @return @ref The mtx_timedlock function returns thrd_success on success, or
* thrd_timedout if the time specified was reached without acquiring the
* requested resource, or thrd_error if the request could not be honored.
*/
int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
/** Try to lock the given mutex.
* The specified mutex shall support either test and return or timeout. If the
* mutex is already locked, the function returns without blocking.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_busy if the resource
* requested is already in use, or @ref thrd_error if the request could not be
* honored.
*/
int mtx_trylock(mtx_t *mtx);
/** Unlock the given mutex.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int mtx_unlock(mtx_t *mtx);
/* Condition variable */
#if defined(_TTHREAD_WIN32_)
typedef struct {
HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */
unsigned int mWaitersCount; /* Count of the number of waiters. */
CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */
} cnd_t;
#else
typedef pthread_cond_t cnd_t;
#endif
/** Create a condition variable object.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_init(cnd_t *cond);
/** Release any resources used by the given condition variable.
* @param cond A condition variable object.
*/
void cnd_destroy(cnd_t *cond);
/** Signal a condition variable.
* Unblocks one of the threads that are blocked on the given condition variable
* at the time of the call. If no threads are blocked on the condition variable
* at the time of the call, the function does nothing and return success.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_signal(cnd_t *cond);
/** Broadcast a condition variable.
* Unblocks all of the threads that are blocked on the given condition variable
* at the time of the call. If no threads are blocked on the condition variable
* at the time of the call, the function does nothing and return success.
* @param cond A condition variable object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_broadcast(cnd_t *cond);
/** Wait for a condition variable to become signaled.
* The function atomically unlocks the given mutex and endeavors to block until
* the given condition variable is signaled by a call to cnd_signal or to
* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
* before it returns.
* @param cond A condition variable object.
* @param mtx A mutex object.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int cnd_wait(cnd_t *cond, mtx_t *mtx);
/** Wait for a condition variable to become signaled.
* The function atomically unlocks the given mutex and endeavors to block until
* the given condition variable is signaled by a call to cnd_signal or to
* cnd_broadcast, or until after the specified time. When the calling thread
* becomes unblocked it locks the mutex before it returns.
* @param cond A condition variable object.
* @param mtx A mutex object.
* @param xt A point in time at which the request will time out (absolute time).
* @return @ref thrd_success upon success, or @ref thrd_timeout if the time
* specified in the call was reached without acquiring the requested resource, or
* @ref thrd_error if the request could not be honored.
*/
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
/* Thread */
#if defined(_TTHREAD_WIN32_)
typedef HANDLE thrd_t;
#else
typedef pthread_t thrd_t;
#endif
/** Thread start function.
* Any thread that is started with the @ref thrd_create() function must be
* started through a function of this type.
* @param arg The thread argument (the @c arg argument of the corresponding
* @ref thrd_create() call).
* @return The thread return value, which can be obtained by another thread
* by using the @ref thrd_join() function.
*/
typedef int (*thrd_start_t)(void *arg);
/** Create a new thread.
* @param thr Identifier of the newly created thread.
* @param func A function pointer to the function that will be executed in
* the new thread.
* @param arg An argument to the thread function.
* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
* be allocated for the thread requested, or @ref thrd_error if the request
* could not be honored.
* @note A threads identifier may be reused for a different thread once the
* original thread has exited and either been detached or joined to another
* thread.
*/
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
/** Identify the calling thread.
* @return The identifier of the calling thread.
*/
thrd_t thrd_current(void);
/** Dispose of any resources allocated to the thread when that thread exits.
* @return thrd_success, or thrd_error on error
*/
int thrd_detach(thrd_t thr);
/** Compare two thread identifiers.
* The function determines if two thread identifiers refer to the same thread.
* @return Zero if the two thread identifiers refer to different threads.
* Otherwise a nonzero value is returned.
*/
int thrd_equal(thrd_t thr0, thrd_t thr1);
/** Terminate execution of the calling thread.
* @param res Result code of the calling thread.
*/
TTHREAD_NORETURN void thrd_exit(int res);
/** Wait for a thread to terminate.
* The function joins the given thread with the current thread by blocking
* until the other thread has terminated.
* @param thr The thread to join with.
* @param res If this pointer is not NULL, the function will store the result
* code of the given thread in the integer pointed to by @c res.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int thrd_join(thrd_t thr, int *res);
/** Put the calling thread to sleep.
* Suspend execution of the calling thread.
* @param duration Interval to sleep for
* @param remaining If non-NULL, this parameter will hold the remaining
* time until time_point upon return. This will
* typically be zero, but if the thread was woken up
* by a signal that is not ignored before duration was
* reached @c remaining will hold a positive time.
* @return 0 (zero) on successful sleep, -1 if an interrupt occurred,
* or a negative value if the operation fails.
*/
int thrd_sleep(const struct timespec *duration, struct timespec *remaining);
/** Yield execution to another thread.
* Permit other threads to run, even if the current thread would ordinarily
* continue to run.
*/
void thrd_yield(void);
/* Thread local storage */
#if defined(_TTHREAD_WIN32_)
typedef DWORD tss_t;
#else
typedef pthread_key_t tss_t;
#endif
/** Destructor function for a thread-specific storage.
* @param val The value of the destructed thread-specific storage.
*/
typedef void (*tss_dtor_t)(void *val);
/** Create a thread-specific storage.
* @param key The unique key identifier that will be set if the function is
* successful.
* @param dtor Destructor function. This can be NULL.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
* @note On Windows, the @c dtor will definitely be called when
* appropriate for threads created with @ref thrd_create. It will be
* called for other threads in most cases, the possible exception being
* for DLLs loaded with LoadLibraryEx. In order to be certain, you
* should use @ref thrd_create whenever possible.
*/
int tss_create(tss_t *key, tss_dtor_t dtor);
/** Delete a thread-specific storage.
* The function releases any resources used by the given thread-specific
* storage.
* @param key The key that shall be deleted.
*/
void tss_delete(tss_t key);
/** Get the value for a thread-specific storage.
* @param key The thread-specific storage identifier.
* @return The value for the current thread held in the given thread-specific
* storage.
*/
void *tss_get(tss_t key);
/** Set the value for a thread-specific storage.
* @param key The thread-specific storage identifier.
* @param val The value of the thread-specific storage to set for the current
* thread.
* @return @ref thrd_success on success, or @ref thrd_error if the request could
* not be honored.
*/
int tss_set(tss_t key, void *val);
#if defined(_TTHREAD_WIN32_)
typedef struct {
LONG volatile status;
CRITICAL_SECTION lock;
} once_flag;
#define ONCE_FLAG_INIT {0,}
#else
#define once_flag pthread_once_t
#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
#endif
/** Invoke a callback exactly once
* @param flag Flag used to ensure the callback is invoked exactly
* once.
* @param func Callback to invoke.
*/
#if defined(_TTHREAD_WIN32_)
void call_once(once_flag *flag, void (*func)(void));
#else
#define call_once(flag,func) pthread_once(flag,func)
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TINYTHREAD_H_ */
-3
View File
@@ -1,11 +1,8 @@
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> // rand #include <stdlib.h> // rand
#include <string.h> #include <string.h>
#ifndef MICROTCP_AMALGAMATION
#include "utils.h" #include "utils.h"
#include "endian.h" #include "endian.h"
#endif
static bool is_hex_digit(char c) static bool is_hex_digit(char c)
{ {
-3
View File
@@ -1,8 +1,5 @@
#include <stdbool.h> #include <stdbool.h>
#ifndef MICROTCP_AMALGAMATION
#include "defs.h" #include "defs.h"
#endif
bool parse_mac(const char *src, size_t len, mac_address_t *mac); bool parse_mac(const char *src, size_t len, mac_address_t *mac);
bool parse_ip(const char *ip, ip_address_t *parsed_ip); bool parse_ip(const char *ip, ip_address_t *parsed_ip);