Single thread (#339)

* single thread

Based on https://github.com/sctplab/usrsctp/pull/38/commits/010fcbc6f193152607dc267c7e7e573c9bffc111

from @ccaughie

* single thread: st_client.c. remove unused variables

* Revert "single thread: st_client.c. remove unused variables"

This reverts commit 1e240a954dcadf6296b9a0f2d1299e07a9e65ca8.

* single thread: st_client.c. remove unused variable

* single thread: s/usrsctp_fire_timer/usrsctp_handle_timers

* single thread: remove duplicated functions

* single thread: remove duplicate check for SCTP_SO_IS_NBIO(so)

* single thread: s/handle_packets/handle_events

* .gitignore: ignore programs/st_client binary

* user_socket.c: remove unnecessary SB_UPCALL check

* programs/client.c: make main() return(-1) after perror()

* programs/st_client.c: trying to make it work (it does not yet)

* programs/st_client.c: replace CLOCKS_PER_SEC with gettimeofday based code

* programs/client.c: revert cosmetic changes

* programs/st_client.c: clean debug prints

* programs/meson.build: add st_client

* Fix warnings in Windows

* programs/st_client.c: use snd 0 instead of 1.

* programs/st_client.c: add usrsctp_setsockopt(s, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(int))
diff --git a/.gitignore b/.gitignore
index f1edda0..2fc78e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,6 +73,7 @@
 programs/http_client
 programs/http_client_upcall
 programs/rtcweb
+programs/st_client
 programs/tsctp
 programs/test_libmgmt
 programs/test_timer
diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt
index b99d181..45c772b 100644
--- a/programs/CMakeLists.txt
+++ b/programs/CMakeLists.txt
@@ -100,6 +100,7 @@
 	http_client.c
 	http_client_upcall.c
 	rtcweb.c
+	st_client.c
 	test_libmgmt.c
 	test_timer.c
 	tsctp.c
diff --git a/programs/Makefile.am b/programs/Makefile.am
index acb2168..794c32e 100644
--- a/programs/Makefile.am
+++ b/programs/Makefile.am
@@ -52,7 +52,8 @@
     ekr_peer.c \
     test_libmgmt.c \
     http_client.c \
-    http_client_upcall.c
+    http_client_upcall.c \
+    st_client.c
 
 noinst_PROGRAMS = \
     test_libmgmt \
@@ -76,7 +77,8 @@
     ekr_peer \
     test_libmgmt \
     http_client \
-    http_client_upcall
+    http_client_upcall \
+    st_client
 
 test_libmgmt_SOURCES = programs_helper.c test_libmgmt.c
 test_libmgmt_LDADD = ../usrsctplib/libusrsctp.la
@@ -120,3 +122,5 @@
 http_client_LDADD = ../usrsctplib/libusrsctp.la
 http_client_upcall_SOURCES = programs_helper.c http_client_upcall.c
 http_client_upcall_LDADD = ../usrsctplib/libusrsctp.la
+st_client_SOURCES = programs_helper.c st_client.c
+st_client_LDADD = ../usrsctplib/libusrsctp.la
diff --git a/programs/Makefile.nmake b/programs/Makefile.nmake
index d426b24..24e9695 100644
--- a/programs/Makefile.nmake
+++ b/programs/Makefile.nmake
@@ -57,7 +57,8 @@
 	ekr_loop_upcall \

 	test_libmgmt \

 	http_client \

-	http_client_upcall

+	http_client_upcall \

+	st_client

 	

 programs_helper.obj : programs_helper.c programs_helper.h

 	cl $(CVARSDLL) $(CFLAGS) -c programs_helper.c

@@ -138,6 +139,10 @@
 	$(CC) $(CFLAGS) $(CVARSDLL) -c http_client_upcall.c

 	link -out:http_client_upcall.exe http_client_upcall.obj programs_helper.obj $(LINKFLAGS)

 

+st_client:
+	$(CC) $(CFLAGS) $(CVARSDLL) -c st_client.c
+	link -out:st_client.exe st_client.obj programs_helper.obj $(LINKFLAGS)
+

 clean:

 	del /F client.exe

 	del /F client.obj

@@ -177,3 +182,5 @@
 	del /F http_client.obj

 	del /F http_client_upcall.exe

 	del /F http_client_upcall.obj

+	del /F st_client.exe

+	del /F st_client.obj

diff --git a/programs/meson.build b/programs/meson.build
index daed941..9cbb0fb 100644
--- a/programs/meson.build
+++ b/programs/meson.build
@@ -5,6 +5,7 @@
 programs = {
     'chargen_server_upcall': files('chargen_server_upcall.c'),
     'client': files('client.c'),
+    'st_client': files('st_client.c'),
     'client_upcall': files('client_upcall.c'),
     'daytime_server': files('daytime_server.c'),
     'daytime_server_upcall': files('daytime_server_upcall.c'),
diff --git a/programs/st_client.c b/programs/st_client.c
new file mode 100644
index 0000000..777e6d2
--- /dev/null
+++ b/programs/st_client.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2011-2013 Michael Tuexen
+ * Copyright (C) 2011-2015 Colin Caughie
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.	IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Usage: st_client local_addr local_port remote_addr remote_port remote_sctp_port
+ */
+
+#ifdef _WIN32
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#else
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+#include <usrsctp.h>
+#include "programs_helper.h"
+
+#define MAX_PACKET_SIZE (1<<16)
+#define BUFFER_SIZE 80
+#define DISCARD_PPID 39
+
+#define TIMER_INTERVAL_MSECS 10
+
+static int connecting = 0;
+static int finish = 0;
+
+static unsigned get_tick_count(void)
+{
+#ifdef _WIN32
+	return GetTickCount();
+#else
+	struct timeval te;
+	gettimeofday(&te, NULL); // get current time
+	unsigned milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds
+	return milliseconds;
+#endif
+}
+
+static void
+handle_events(int sock, struct socket* s, void* sconn_addr)
+{
+	char *dump_buf;
+	ssize_t length;
+	char buf[MAX_PACKET_SIZE];
+
+	fd_set rfds;
+	struct timeval tv;
+
+	unsigned next_fire_time = get_tick_count();
+	unsigned last_fire_time = next_fire_time;
+
+	while (!finish) {
+
+		unsigned now = get_tick_count();
+		int wait_time;
+
+		if ((int) (now - next_fire_time) > 0) {
+			usrsctp_handle_timers(now - last_fire_time);
+			last_fire_time = now;
+			next_fire_time = now + TIMER_INTERVAL_MSECS;
+		}
+
+		wait_time = next_fire_time - now;
+		tv.tv_sec = wait_time / 1000;
+		tv.tv_usec = (wait_time % 1000) * 1000;
+
+		FD_ZERO(&rfds);
+		FD_SET(sock, &rfds);
+
+		select(sock + 1, &rfds, NULL, NULL, &tv);
+
+		if (FD_ISSET(sock, &rfds)) {
+			length = recv(sock, buf, MAX_PACKET_SIZE, 0);
+
+			if (length > 0) {
+				if ((dump_buf = usrsctp_dumppacket(buf, (size_t)length, SCTP_DUMP_INBOUND)) != NULL) {
+					fprintf(stderr, "%s", dump_buf);
+					usrsctp_freedumpbuffer(dump_buf);
+				}
+				usrsctp_conninput(sconn_addr, buf, (size_t)length, 0);
+			}
+		}
+	}
+}
+
+static void on_connect(struct socket* s)
+{
+	struct sctp_sndinfo sndinfo;
+	char buffer[BUFFER_SIZE];
+
+	memset(buffer, 'A', BUFFER_SIZE);
+	sndinfo.snd_sid = 0;
+	sndinfo.snd_flags = 0;
+	sndinfo.snd_ppid = htonl(DISCARD_PPID);
+	sndinfo.snd_context = 0;
+	sndinfo.snd_assoc_id = 0;
+	if (usrsctp_sendv(s, buffer, BUFFER_SIZE, NULL, 0, (void *)&sndinfo,
+	                  (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0) < 0) {
+		perror("usrsctp_sendv");
+	}
+}
+
+static void on_socket_readable(struct socket* s) {
+	char buffer[BUFFER_SIZE];
+	union sctp_sockstore addr;
+	socklen_t fromlen = sizeof(addr);
+	struct sctp_rcvinfo rcv_info;
+	socklen_t infolen = sizeof(rcv_info);
+	unsigned int infotype = 0;
+	int flags = 0;
+	ssize_t retval;
+
+	/* Keep reading until there is no more data */
+	for (;;) {
+		retval = usrsctp_recvv(s, buffer, sizeof(buffer), (struct sockaddr*) &addr,
+				&fromlen, &rcv_info, &infolen, &infotype, &flags);
+
+		if (retval < 0) {
+			if (errno != EWOULDBLOCK) {
+				perror("usrsctp_recvv");
+				finish = 1;
+			}
+			return;
+		} else if (retval == 0) {
+			printf("socket was disconnected\n");
+			finish = 1;
+			return;
+		}
+
+		if (flags & MSG_NOTIFICATION) {
+			printf("Notification of length %d received.\n", (int)retval);
+		} else {
+			printf("Msg of length %d received via %p:%u on stream %d with SSN %u and TSN %u, PPID %d, context %u.\n",
+			       (int)retval,
+			       addr.sconn.sconn_addr,
+			       ntohs(addr.sconn.sconn_port),
+			       rcv_info.rcv_sid,
+			       rcv_info.rcv_ssn,
+			       rcv_info.rcv_tsn,
+			       ntohl(rcv_info.rcv_ppid),
+			       rcv_info.rcv_context);
+		}
+	}
+}
+
+static void handle_upcall(struct socket *s, void *arg, int flags)
+{
+    int events = usrsctp_get_events(s);
+
+	if (connecting) {
+		if (events & SCTP_EVENT_ERROR) {
+			connecting = 0;
+			finish = 1;
+		} else if (events & SCTP_EVENT_WRITE) {
+			connecting = 0;
+			on_connect(s);
+		}
+
+		return;
+	}
+
+	if (events & SCTP_EVENT_READ) {
+		on_socket_readable(s);
+	}
+}
+
+static int
+conn_output(void *addr, void *buf, size_t length, uint8_t tos, uint8_t set_df)
+{
+	char *dump_buf;
+#ifdef _WIN32
+	SOCKET *fdp;
+#else
+	int *fdp;
+#endif
+
+#ifdef _WIN32
+	fdp = (SOCKET *)addr;
+#else
+	fdp = (int *)addr;
+#endif
+	if ((dump_buf = usrsctp_dumppacket(buf, length, SCTP_DUMP_OUTBOUND)) != NULL) {
+		fprintf(stderr, "%s", dump_buf);
+		usrsctp_freedumpbuffer(dump_buf);
+	}
+#ifdef _WIN32
+	if (send(*fdp, buf, (int)length, 0) == SOCKET_ERROR) {
+		return (WSAGetLastError());
+#else
+	if (send(*fdp, buf, length, 0) < 0) {
+		return (errno);
+#endif
+	} else {
+		return (0);
+	}
+}
+
+/* Usage: st_client local_addr local_port remote_addr remote_port remote_sctp_port */
+int
+main(int argc, char *argv[])
+{
+	struct sockaddr_in sin;
+	struct sockaddr_conn sconn;
+#ifdef _WIN32
+	SOCKET fd;
+#else
+	int fd;
+#endif
+	struct socket *s;
+	int retval;
+#ifdef _WIN32
+	WSADATA wsaData;
+#endif
+
+	if (argc < 6) {
+		printf("Usage: st_client local_addr local_port remote_addr remote_port remote_sctp_port\n");
+		return (-1);
+	}
+
+#ifdef _WIN32
+	if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
+		printf("WSAStartup failed\n");
+		return (-1);
+	}
+#endif
+	usrsctp_init_nothreads(0, conn_output, debug_printf);
+	/* set up a connected UDP socket */
+#ifdef _WIN32
+	if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
+		printf("socket() failed with error: %ld\n", WSAGetLastError());
+		return (-1);
+	}
+#else
+	if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+		perror("socket");
+		return (-1);
+	}
+#endif
+	memset(&sin, 0, sizeof(struct sockaddr_in));
+	sin.sin_family = AF_INET;
+#ifdef HAVE_SIN_LEN
+	sin.sin_len = sizeof(struct sockaddr_in);
+#endif
+	sin.sin_port = htons(atoi(argv[2]));
+	if (!inet_pton(AF_INET, argv[1], &sin.sin_addr.s_addr)){
+		printf("error: invalid address\n");
+		return (-1);
+	}
+#ifdef _WIN32
+	if (bind(fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
+		printf("bind() failed with error: %ld\n", WSAGetLastError());
+		return (-1);
+	}
+#else
+	if (bind(fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0) {
+		perror("bind");
+		return (-1);
+	}
+#endif
+	memset(&sin, 0, sizeof(struct sockaddr_in));
+	sin.sin_family = AF_INET;
+#ifdef HAVE_SIN_LEN
+	sin.sin_len = sizeof(struct sockaddr_in);
+#endif
+	sin.sin_port = htons(atoi(argv[4]));
+	if (!inet_pton(AF_INET, argv[3], &sin.sin_addr.s_addr)){
+		printf("error: invalid address\n");
+		return (-1);
+	}
+#ifdef _WIN32
+	if (connect(fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
+		printf("connect() failed with error: %ld\n", WSAGetLastError());
+		return (-1);
+	}
+#else
+	if (connect(fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0) {
+		perror("connect");
+		return (-1);
+	}
+#endif
+#ifdef SCTP_DEBUG
+	usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_NONE);
+#endif
+	usrsctp_sysctl_set_sctp_ecn_enable(0);
+	usrsctp_register_address((void *)&fd);
+
+	if ((s = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, NULL, NULL, 0, NULL)) == NULL) {
+		perror("usrsctp_socket");
+		return (-1);
+	}
+
+	const int on = 1;
+
+	usrsctp_setsockopt(s, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(int));
+
+	usrsctp_set_non_blocking(s, 1);
+    usrsctp_set_upcall(s, handle_upcall, NULL);
+
+	memset(&sconn, 0, sizeof(struct sockaddr_conn));
+	sconn.sconn_family = AF_CONN;
+#ifdef HAVE_SCONN_LEN
+	sconn.sconn_len = sizeof(struct sockaddr_conn);
+#endif
+	sconn.sconn_port = htons(0);
+	sconn.sconn_addr = NULL;
+	if (usrsctp_bind(s, (struct sockaddr *)&sconn, sizeof(struct sockaddr_conn)) < 0) {
+		perror("usrsctp_bind");
+		return (-1);
+	}
+
+	memset(&sconn, 0, sizeof(struct sockaddr_conn));
+	sconn.sconn_family = AF_CONN;
+#ifdef HAVE_SCONN_LEN
+	sconn.sconn_len = sizeof(struct sockaddr_conn);
+#endif
+	sconn.sconn_port = htons(atoi(argv[5]));
+	sconn.sconn_addr = &fd;
+
+	retval = usrsctp_connect(s, (struct sockaddr *)&sconn, sizeof(struct sockaddr_conn));
+
+	if (retval < 0 && errno != EWOULDBLOCK && errno != EINPROGRESS) {
+		perror("usrsctp_connect");
+		return (-1);
+	}
+
+	connecting = 1;
+
+	handle_events(fd, s, sconn.sconn_addr);
+
+	return 0;
+}
diff --git a/usrsctplib/netinet/sctp_callout.c b/usrsctplib/netinet/sctp_callout.c
index b12b4b2..3ba5270 100755
--- a/usrsctplib/netinet/sctp_callout.c
+++ b/usrsctplib/netinet/sctp_callout.c
@@ -217,7 +217,7 @@
 	return (1);
 }
 
-static void
+void
 sctp_handle_tick(int delta)
 {
 	sctp_os_timer_t *c;
diff --git a/usrsctplib/netinet/sctp_callout.h b/usrsctplib/netinet/sctp_callout.h
index 814bd06..3ac6c5b 100755
--- a/usrsctplib/netinet/sctp_callout.h
+++ b/usrsctplib/netinet/sctp_callout.h
@@ -103,6 +103,7 @@
 void sctp_os_timer_init(sctp_os_timer_t *tmr);
 void sctp_os_timer_start(sctp_os_timer_t *, int, void (*)(void *), void *);
 int sctp_os_timer_stop(sctp_os_timer_t *);
+void sctp_handle_tick(int delta);
 
 #define SCTP_OS_TIMER_INIT	sctp_os_timer_init
 #define SCTP_OS_TIMER_START	sctp_os_timer_start
diff --git a/usrsctplib/netinet/sctp_pcb.c b/usrsctplib/netinet/sctp_pcb.c
index f5c4e3a..4ea0845 100755
--- a/usrsctplib/netinet/sctp_pcb.c
+++ b/usrsctplib/netinet/sctp_pcb.c
@@ -6635,7 +6635,7 @@
 #endif
 
 void
-sctp_pcb_init()
+sctp_pcb_init(int start_threads)
 {
 	/*
 	 * SCTP initialization for the PCB structures should be called by
@@ -6851,7 +6851,8 @@
 	mbuf_initialize(NULL);
 	atomic_init();
 #if defined(INET) || defined(INET6)
-	recv_thread_init();
+	if (start_threads)
+		recv_thread_init();
 #endif
 #endif
 }
diff --git a/usrsctplib/netinet/sctp_pcb.h b/usrsctplib/netinet/sctp_pcb.h
index dad09e0..16b19c0 100755
--- a/usrsctplib/netinet/sctp_pcb.h
+++ b/usrsctplib/netinet/sctp_pcb.h
@@ -843,7 +843,7 @@
 
 int sctp_del_remote_addr(struct sctp_tcb *, struct sockaddr *);
 
-void sctp_pcb_init(void);
+void sctp_pcb_init(int);
 
 void sctp_pcb_finish(void);
 
diff --git a/usrsctplib/netinet/sctp_usrreq.c b/usrsctplib/netinet/sctp_usrreq.c
index 0912e3e..5ff7a2a 100755
--- a/usrsctplib/netinet/sctp_usrreq.c
+++ b/usrsctplib/netinet/sctp_usrreq.c
@@ -77,7 +77,7 @@
 #if defined(__Userspace__)
 sctp_init(uint16_t port,
           int (*conn_output)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
-          void (*debug_printf)(const char *format, ...))
+          void (*debug_printf)(const char *format, ...), int start_threads)
 #elif defined(__APPLE__) && (!defined(APPLE_LEOPARD) && !defined(APPLE_SNOWLEOPARD) &&!defined(APPLE_LION) && !defined(APPLE_MOUNTAINLION))
 sctp_init(struct protosw *pp SCTP_UNUSED, struct domain *dp SCTP_UNUSED)
 #else
@@ -151,9 +151,10 @@
 	SCTP_BASE_VAR(debug_printf) = debug_printf;
 	SCTP_BASE_VAR(crc32c_offloaded) = 0;
 #endif
-	sctp_pcb_init();
+	sctp_pcb_init(start_threads);
 #if defined(__Userspace__)
-	sctp_start_timer();
+	if (start_threads)
+		sctp_start_timer();
 #endif
 #if defined(SCTP_PACKET_LOGGING)
 	SCTP_BASE_VAR(packet_log_writers) = 0;
diff --git a/usrsctplib/netinet/sctp_var.h b/usrsctplib/netinet/sctp_var.h
index bc4afe9..ead9e9c 100755
--- a/usrsctplib/netinet/sctp_var.h
+++ b/usrsctplib/netinet/sctp_var.h
@@ -454,7 +454,7 @@
 #if defined(__Userspace__)
 void sctp_init(uint16_t,
                int (*)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
-               void (*)(const char *, ...));
+               void (*)(const char *, ...), int start_threads);
 #elif defined(__FreeBSD__) && __FreeBSD_version < 902000
 void sctp_init __P((void));
 #elif defined(__APPLE__) && (!defined(APPLE_LEOPARD) && !defined(APPLE_SNOWLEOPARD) &&!defined(APPLE_LION) && !defined(APPLE_MOUNTAINLION))
diff --git a/usrsctplib/user_socket.c b/usrsctplib/user_socket.c
index bec0226..bde5428 100755
--- a/usrsctplib/user_socket.c
+++ b/usrsctplib/user_socket.c
@@ -39,6 +39,7 @@
 #include <netinet/sctp_sysctl.h>
 #include <netinet/sctp_input.h>
 #include <netinet/sctp_peeloff.h>
+#include <netinet/sctp_callout.h>
 #include <netinet/sctp_crc32.h>
 #ifdef INET6
 #include <netinet6/sctp6_var.h>
@@ -77,11 +78,7 @@
 extern int sctp_attach(struct socket *so, int proto, uint32_t vrf_id);
 extern int sctpconn_attach(struct socket *so, int proto, uint32_t vrf_id);
 
-void
-usrsctp_init(uint16_t port,
-             int (*conn_output)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
-             void (*debug_printf)(const char *format, ...))
-{
+static void init_sync(void) {
 #if defined(__Userspace_os_Windows)
 #if defined(INET) || defined(INET6)
 	WSADATA wsaData;
@@ -104,7 +101,25 @@
 	pthread_mutexattr_destroy(&mutex_attr);
 	pthread_cond_init(&accept_cond, NULL);
 #endif
-	sctp_init(port, conn_output, debug_printf);
+}
+
+void
+usrsctp_init(uint16_t port,
+             int (*conn_output)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
+             void (*debug_printf)(const char *format, ...))
+{
+	init_sync();
+	sctp_init(port, conn_output, debug_printf, 1);
+}
+
+
+void
+usrsctp_init_nothreads(uint16_t port,
+		       int (*conn_output)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
+		       void (*debug_printf)(const char *format, ...))
+{
+	init_sync();
+	sctp_init(port, conn_output, debug_printf, 0);
 }
 
 
@@ -1564,8 +1579,6 @@
 #endif
 	}
 	SOCKBUF_UNLOCK(sb);
-	/*__Userspace__ what todo about so_upcall?*/
-
 }
 #else /* kernel version for reference */
 /*
@@ -3515,6 +3528,11 @@
 	return;
 }
 
+void usrsctp_handle_timers(int delta)
+{
+	sctp_handle_tick(delta);
+}
+
 int
 usrsctp_get_events(struct socket *so)
 {
diff --git a/usrsctplib/usrsctp.h b/usrsctplib/usrsctp.h
index 0533c49..d8b4593 100644
--- a/usrsctplib/usrsctp.h
+++ b/usrsctplib/usrsctp.h
@@ -892,6 +892,11 @@
              int (*)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
              void (*)(const char *format, ...));
 
+void
+usrsctp_init_nothreads(uint16_t,
+		       int (*)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
+		       void (*)(const char *format, ...));
+
 struct socket *
 usrsctp_socket(int domain, int type, int protocol,
                int (*receive_cb)(struct socket *sock, union sctp_sockstore addr, void *data,
@@ -1034,6 +1039,9 @@
 usrsctp_get_events(struct socket *so);
 
 
+void
+usrsctp_handle_timers(int delta);
+
 #define SCTP_DUMP_OUTBOUND 1
 #define SCTP_DUMP_INBOUND  0