Merge branch 'feature/rtc' into develop

pull/1748/head
winlin 5 years ago
commit f2b575e841

@ -31,6 +31,14 @@ The branch [srs](https://github.com/ossrs/state-threads/tree/srs) will be patche
* API reference: http://ossrs.github.io/state-threads/docs/reference.html
* Programming notes: http://ossrs.github.io/state-threads/docs/notes.html
## Analysis
* About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg).
* About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg)
* About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25).
* About the scheduler, read [#13-scheduler](https://github.com/ossrs/state-threads/issues/13#issuecomment-616025527).
* About the IO event system, read [#13-IO](https://github.com/ossrs/state-threads/issues/13#issuecomment-616096568).
## Usage
Get code:
@ -87,10 +95,4 @@ Important cli options:
1. `--track-origins=<yes|no> [default: no]`, Controls whether Memcheck tracks the origin of uninitialised values. By default, it does not, which means that although it can tell you that an uninitialised value is being used in a dangerous way, it cannot tell you where the uninitialised value came from. This often makes it difficult to track down the root problem.
1. `--show-reachable=<yes|no> , --show-possibly-lost=<yes|no>`, to show the using memory.
## Analysis
1. About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg).
1. About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg)
1. About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25).
Winlin 2016

@ -438,6 +438,10 @@ fi
# cherrypy for http hooks callback, CherryPy-3.2.4
#####################################################################################
if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
# Detect python or python2
python --version >/dev/null 2>&1 && SYS_PYTHON=python;
python2 --version >/dev/null 2>&1 && SYS_PYTHON=python2;
# Install cherrypy for api server.
if [[ -f ${SRS_OBJS}/${SRS_PLATFORM}/CherryPy-3.2.4/setup.py ]]; then
echo "CherryPy-3.2.4 is ok.";
else
@ -445,7 +449,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
(
rm -rf ${SRS_OBJS}/CherryPy-3.2.4 && cd ${SRS_OBJS}/${SRS_PLATFORM} &&
unzip -q ../../3rdparty/CherryPy-3.2.4.zip && cd CherryPy-3.2.4 &&
python setup.py install --user --prefix=''
$SYS_PYTHON setup.py install --user --prefix=''
)
fi
# check status

@ -0,0 +1,2 @@
server
client

@ -0,0 +1,16 @@
.PHONY: default clean
default: server client
server: server.cpp ../../objs/st/libst.a
g++ -g -O0 -I../../objs/st/ $^ -o $@
client: client.cpp ../../objs/st/libst.a
g++ -g -O0 -I../../objs/st/ $^ -o $@
../../objs/st/libst.a: ../../Makefile
(cd ../../ && $(MAKE) st)
clean:
rm -f server client ../../objs/st/libst.a

@ -0,0 +1,315 @@
#include <st.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <linux/version.h>
// @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
#include <sys/epoll.h>
// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c
#include <linux/errqueue.h>
#ifndef SO_EE_ORIGIN_ZEROCOPY
#define SO_EE_ORIGIN_ZEROCOPY 5
#endif
#ifndef SO_ZEROCOPY
#define SO_ZEROCOPY 60
#endif
#ifndef SO_EE_CODE_ZEROCOPY_COPIED
#define SO_EE_CODE_ZEROCOPY_COPIED 1
#endif
#ifndef MSG_ZEROCOPY
#define MSG_ZEROCOPY 0x4000000
#endif
#include <netinet/udp.h>
// Define macro for UDP GSO.
// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/udpgso.c
#ifndef UDP_SEGMENT
#define UDP_SEGMENT 103
#endif
void* receiver(void* arg)
{
st_netfd_t stfd = (st_netfd_t)arg;
for (;;) {
sockaddr_in peer;
memset(&peer, 0, sizeof(sockaddr_in));
char buf[1500];
memset(buf, 0, sizeof(buf));
iovec iov;
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msghdr msg;
memset(&msg, 0, sizeof(msghdr));
msg.msg_name = (sockaddr_in*)&peer;
msg.msg_namelen = sizeof(sockaddr_in);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
int r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
assert(r0 > 0);
printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
msg.msg_flags, msg.msg_iov->iov_base);
}
return NULL;
}
void parse_reception(st_netfd_t stfd, int nn_confirm)
{
int left = nn_confirm;
while (left > 0) {
msghdr msg;
memset(&msg, 0, sizeof(msghdr));
// Reception from kernel, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
// See do_recv_completion at https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c#L393
char control[100];
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
// Note that the r0 is 0, the reception is in the control.
int r0 = st_recvmsg(stfd, &msg, MSG_ERRQUEUE, ST_UTIME_NO_TIMEOUT);
assert(r0 >= 0);
assert(msg.msg_flags == MSG_ERRQUEUE);
// Notification parsing, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-parsing
cmsghdr* cm = CMSG_FIRSTHDR(&msg);
assert(cm->cmsg_level == SOL_IP || cm->cmsg_type == IP_RECVERR);
sock_extended_err* serr = (sock_extended_err*)(void*)CMSG_DATA(cm);
assert(serr->ee_errno == 0 && serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY);
uint32_t hi = serr->ee_data;
uint32_t lo = serr->ee_info;
uint32_t range = hi - lo + 1;
left -= range;
printf("Reception %d bytes, flags %#x, cmsg(level %#x, type %#x), serr(errno %#x, origin %#x, code %#x), range %d [%d, %d]\n",
msg.msg_controllen, msg.msg_flags, cm->cmsg_level, cm->cmsg_type, serr->ee_errno, serr->ee_origin, serr->ee_code, range, lo, hi);
// Defered Copies, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#deferred-copies
if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) {
printf("Warning: Defered copies, should stop zerocopy\n");
}
}
}
void usage(int argc, char** argv)
{
printf("Usage: %s <options>\n", argv[0]);
printf("Options:\n");
printf(" --help Print this help and exit.\n");
printf(" --host=string The host to send to.\n");
printf(" --port=int The port to send to.\n");
printf(" --pong=bool Whether response pong, true|false\n");
printf(" --zerocopy=bool Whether use zerocopy to sendmsg, true|false\n");
printf(" --copy=int The copies of message, 1 means sendmmsg(msg+msg)\n");
printf(" --loop=int The number of loop to send out messages\n");
printf(" --batch=bool Whether read reception by batch, true|false\n");
printf(" --mix=bool Whether mix msg with zerocopy and those without, true|false\n");
printf(" --size=int Each message size in bytes.\n");
printf(" --gso=int The GSO size in bytes, 0 to disable it.\n");
printf(" --iovs=int The number of iovs to send, at least 1.\n");
printf(" --sndbuf=int The SO_SNDBUF size in bytes, 0 to ignore.\n");
printf("For example:\n");
printf(" %s --host=127.0.0.1 --port=8000 --pong=true --zerocopy=true --copy=0 --loop=1 --batch=true --mix=true --size=1400 --gso=0 --iovs=1 --sndbuf=0\n", argv[0]);
}
int main(int argc, char** argv)
{
option longopts[] = {
{ "host", required_argument, NULL, 'o' },
{ "port", required_argument, NULL, 'p' },
{ "pong", required_argument, NULL, 'n' },
{ "zerocopy", required_argument, NULL, 'z' },
{ "copy", required_argument, NULL, 'c' },
{ "loop", required_argument, NULL, 'l' },
{ "batch", required_argument, NULL, 'b' },
{ "mix", required_argument, NULL, 'm' },
{ "size", required_argument, NULL, 's' },
{ "gso", required_argument, NULL, 'g' },
{ "iovs", required_argument, NULL, 'i' },
{ "sndbuf", required_argument, NULL, 'u' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
char* host = NULL; char ch;
int port = 0; int nn_copies = 0; int loop = 1; int size = 1500; int gso = 0; int nn_iovs = 0; int sndbuf = 0;
bool pong = false; bool zerocopy = false; bool batch = false; bool mix = false;
while ((ch = getopt_long(argc, argv, "o:p:n:z:c:l:b:m:s:g:u:h", longopts, NULL)) != -1) {
switch (ch) {
case 'o': host = (char*)optarg; break;
case 'p': port = atoi(optarg); break;
case 'n': pong = !strcmp(optarg,"true"); break;
case 'z': zerocopy = !strcmp(optarg,"true"); break;
case 'c': nn_copies = atoi(optarg); break;
case 'l': loop = atoi(optarg); break;
case 'b': batch = !strcmp(optarg,"true"); break;
case 'm': mix = !strcmp(optarg,"true"); break;
case 's': size = atoi(optarg); break;
case 'g': gso = atoi(optarg); break;
case 'i': nn_iovs = atoi(optarg); break;
case 'u': sndbuf = atoi(optarg); break;
case 'h': usage(argc, argv); exit(0);
default: usage(argc, argv); exit(-1);
}
}
printf("Server listen %s:%d, pong %d, zerocopy %d, copies %d, loop %d, batch %d, mix %d, size %d, gso %d, iovs %d, sndbuf %d\n",
host, port, pong, zerocopy, nn_copies, loop, batch, mix, size, gso, nn_iovs, sndbuf);
if (!host || !port || !nn_iovs) {
usage(argc, argv);
exit(-1);
}
assert(!st_set_eventsys(ST_EVENTSYS_ALT));
assert(!st_init());
int fd = socket(PF_INET, SOCK_DGRAM, 0);
assert(fd > 0);
// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c
if (zerocopy) {
int one = 1;
int r0 = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one));
// MSG_ZEROCOPY for UDP was added in commit b5947e5d1e71 ("udp: msg_zerocopy") in Linux 5.0.
// @see https://lore.kernel.org/netdev/CA+FuTSfBFqRViKfG5crEv8xLMgAkp3cZ+yeuELK5TVv61xT=Yw@mail.gmail.com/
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
if (r0 == -1) {
printf("MSG_ZEROCOPY should be kernel 5.0+, kernel %#x, errno=%d\n", LINUX_VERSION_CODE, 524);
exit(-1);
}
#endif
assert(!r0);
printf("epoll events EPOLLERR=%#x, EPOLLHUP=%#x\n", EPOLLERR, EPOLLHUP);
}
if (true) {
int dv = 0;
socklen_t len = sizeof(dv);
int r0 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &dv, &len);
int r1 = 0;
if (sndbuf > 0) {
r1 = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
}
int nv = 0;
int r2 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &nv, &len);
printf("socket SO_SNDBUF default=%d, user=%d, now=%d, r0=%d, r1=%d, r2=%d\n", dv, sndbuf, nv, r0, r1, r2);
}
st_netfd_t stfd = st_netfd_open_socket(fd);
assert(stfd);
printf("Client fd=%d\n", fd);
if (pong) {
st_thread_t r0 = st_thread_create(receiver, stfd, 0, 0);
assert(r0);
}
sockaddr_in peer;
memset(&peer, 0, sizeof(sockaddr_in));
peer.sin_family = AF_INET;
peer.sin_port = htons(port);
peer.sin_addr.s_addr = inet_addr(host);
char* buf = new char[size];
memset(buf, 0, size);
memcpy(buf, "Hello", size < 5? size : 5);
iovec iov;
iov.iov_base = buf;
iov.iov_len = size;
int nn_confirm = 0;
for (int k = 0; k < loop; k++) {
msghdr msg;
memset(&msg, 0, sizeof(msghdr));
msg.msg_name = (sockaddr_in*)&peer;
msg.msg_namelen = sizeof(sockaddr_in);
msg.msg_iov = new iovec[nn_iovs];
msg.msg_iovlen = nn_iovs;
for (int i = 0; i < nn_iovs; i++) {
iovec* p = msg.msg_iov + i;
memcpy(p, &iov, sizeof(iovec));
}
if (gso > 0) {
msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
if (!msg.msg_control) {
msg.msg_control = new char[msg.msg_controllen];
}
cmsghdr* cm = CMSG_FIRSTHDR(&msg);
cm->cmsg_level = SOL_UDP;
cm->cmsg_type = UDP_SEGMENT;
cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
*((uint16_t*)CMSG_DATA(cm)) = gso;
}
int r0;
if (nn_copies == 0) {
if (zerocopy) {
r0 = st_sendmsg(stfd, &msg, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
} else {
r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
}
} else {
mmsghdr* hdrs = new mmsghdr[nn_copies + 1];
for (int i = 0; i < nn_copies + 1; i++) {
mmsghdr* p = hdrs + i;
memcpy(&p->msg_hdr, &msg, sizeof(msghdr));
p->msg_len = 0;
}
if (zerocopy) {
r0 = st_sendmmsg(stfd, hdrs, nn_copies + 1, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
} else {
r0 = st_sendmmsg(stfd, hdrs, nn_copies + 1, 0, ST_UTIME_NO_TIMEOUT);
}
}
if (r0 > 0) {
printf("Ping %s:%d %d bytes, control %d, copies=%d, r0=%d, %s\n", host, port, iov.iov_len * nn_iovs,
msg.msg_controllen, nn_copies, r0, msg.msg_iov->iov_base);
} else {
printf("Ping %d bytes, error r0=%d, errno=%d\n", iov.iov_len * nn_iovs, r0, errno); exit(1);
}
if (zerocopy && !batch) {
parse_reception(stfd, r0);
} else {
nn_confirm += r0;
}
if (mix) {
r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
assert(r0 > 0);
printf("Mix %s:%d %d bytes, r0=%d, %s\n", host, port, iov.iov_len * nn_iovs, r0, msg.msg_iov->iov_base);
}
}
// @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-batching
if (batch) {
parse_reception(stfd, nn_confirm);
}
st_sleep(-1);
return 0;
}

@ -0,0 +1,155 @@
#include <st.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
struct message {
st_netfd_t stfd;
sockaddr_in peer;
int delay;
};
void* sender(void* arg)
{
message* p = (message*)arg;
int delay = p->delay;
if (delay > 0) {
st_usleep(delay * 1000);
}
msghdr msg;
memset(&msg, 0, sizeof(msghdr));
sockaddr_in peer = p->peer;
msg.msg_name = (sockaddr_in*)&peer;
msg.msg_namelen = sizeof(sockaddr_in);
char buf[] = "World";
iovec iov;
memset(&iov, 0, sizeof(iovec));
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
st_netfd_t stfd = p->stfd;
int r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
assert(r0 > 0);
printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
msg.msg_flags, msg.msg_iov->iov_base);
return NULL;
}
void usage(int argc, char** argv)
{
printf("Usage: %s <options>\n", argv[0]);
printf("Options:\n");
printf(" --help Print this help and exit.\n");
printf(" --host=string The host to send to.\n");
printf(" --port=int The port to send to.\n");
printf(" --pong=bool Whether response pong, true|false\n");
printf(" --delay=int The delay in ms to response pong.\n");
printf("For example:\n");
printf(" %s --host=0.0.0.0 --port=8000 --pong --delay=100\n", argv[0]);
}
int main(int argc, char** argv)
{
option longopts[] = {
{ "host", required_argument, NULL, 'o' },
{ "port", required_argument, NULL, 'p' },
{ "pong", required_argument, NULL, 'n' },
{ "delay", required_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
char* host = NULL; char ch;
int port = 0; int delay = 0; bool pong = false;
while ((ch = getopt_long(argc, argv, "o:p:n:d:h", longopts, NULL)) != -1) {
switch (ch) {
case 'o': host = (char*)optarg; break;
case 'p': port = atoi(optarg); break;
case 'n': pong = !strcmp(optarg,"true"); break;
case 'd': delay = atoi(optarg); break;
case 'h': usage(argc, argv); exit(0);
default: usage(argc, argv); exit(-1);
}
}
printf("Server listen %s:%d, pong %d, delay: %dms\n", host, port, pong, delay);
if (!host || !port) {
usage(argc, argv);
exit(-1);
}
assert(!st_set_eventsys(ST_EVENTSYS_ALT));
assert(!st_init());
int fd = socket(PF_INET, SOCK_DGRAM, 0);
assert(fd > 0);
int n = 1;
int r0 = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n));
assert(!r0);
sockaddr_in addr;
memset(&addr, 0, sizeof(sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(host);
r0 = bind(fd, (sockaddr *)&addr, sizeof(sockaddr_in));
assert(!r0);
st_netfd_t stfd = st_netfd_open_socket(fd);
assert(stfd);
printf("Listen at udp://%s:%d, fd=%d\n", host, port, fd);
msghdr msg;
memset(&msg, 0, sizeof(msghdr));
sockaddr_in peer;
memset(&peer, 0, sizeof(sockaddr_in));
msg.msg_name = (sockaddr_in*)&peer;
msg.msg_namelen = sizeof(sockaddr_in);
char buf[1500];
memset(buf, 0, sizeof(buf));
iovec iov;
memset(&iov, 0, sizeof(iovec));
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
int nn_msgs = 0;
while (true) {
r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
assert(r0 > 0);
printf("#%d, From %s:%d %d bytes, flags %#x, %s\n", nn_msgs++, inet_ntoa(peer.sin_addr), ntohs(peer.sin_port),
r0, msg.msg_flags, msg.msg_iov->iov_base);
if (pong) {
message* msg = new message();
msg->stfd = stfd;
msg->peer = peer;
msg->delay = delay;
st_thread_t r0 = st_thread_create(sender, msg, 0, 0);
assert(r0);
}
}
return 0;
}

@ -1,100 +0,0 @@
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Netscape Portable Runtime library.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Silicon Graphics, Inc.
#
# Portions created by SGI are Copyright (C) 2000-2001 Silicon
# Graphics, Inc. All Rights Reserved.
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
##########################
# Target dir and cc:
CC = cc
TARGETDIR = objs
##########################
# Supported OSes:
OS = LINUX
ifneq ($(shell test -f /usr/include/sys/epoll.h && echo yes), yes)
default:
@echo "epoll not found"
@exit 1
endif
EXTRA_OBJS = $(TARGETDIR)/md.o
CFLAGS =
OTHER_FLAGS += -Wall -g -O0
DEFINES = -D$(OS) -DDEBUG -DMD_HAVE_EPOLL -DMALLOC_STACK
##########################
# Other possible defines:
# To use malloc(3) instead of mmap(2) for stack allocation:
# DEFINES += -DMALLOC_STACK
#
# To provision more than the default 16 thread-specific-data keys
# (but not too many!):
# DEFINES += -DST_KEYS_MAX=<n>
#
# Note that you can also add these defines by specifying them as
# make/gmake arguments (without editing this Makefile). For example:
#
# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL <target>
#
##########################
CFLAGS += $(DEFINES) $(OTHER_FLAGS) $(EXTRA_CFLAGS)
OBJS = $(TARGETDIR)/sched.o \
$(TARGETDIR)/stk.o \
$(TARGETDIR)/sync.o \
$(TARGETDIR)/key.o \
$(TARGETDIR)/io.o \
$(TARGETDIR)/event.o \
$(TARGETDIR)/srs.o
OBJS += $(EXTRA_OBJS)
SRS = $(TARGETDIR)/srs
linux-debug: all
all: $(TARGETDIR) $(SRS)
$(TARGETDIR):
if [ ! -d $(TARGETDIR) ]; then mkdir $(TARGETDIR); fi
$(SRS): $(OBJS)
$(CC) $(CFLAGS) -o $@ $(OBJS)
$(TARGETDIR)/md.o: md.S
$(CC) $(CFLAGS) -c $< -o $@
$(TARGETDIR)/%.o: %.c common.h md.h Makefile
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(TARGETDIR)

@ -1,445 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
*
* Portions created by SGI are Copyright (C) 2000-2001 Silicon
* Graphics, Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* This file is derived directly from Netscape Communications Corporation,
* and consists of extensive modifications made during the year(s) 1999-2000.
*/
#ifndef __ST_COMMON_H__
#define __ST_COMMON_H__
#include <stddef.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <setjmp.h>
/* Enable assertions only if DEBUG is defined */
#ifndef DEBUG
#define NDEBUG
#endif
#include <assert.h>
#define ST_ASSERT(expr) assert(expr)
#define ST_BEGIN_MACRO {
#define ST_END_MACRO }
#ifdef DEBUG
#define ST_HIDDEN /*nothing*/
#else
#define ST_HIDDEN static
#endif
#include "public.h"
#include "md.h"
/*****************************************
* Circular linked list definitions
*/
typedef struct _st_clist {
struct _st_clist *next;
struct _st_clist *prev;
} _st_clist_t;
/* Insert element "_e" into the list, before "_l" */
#define ST_INSERT_BEFORE(_e,_l) \
ST_BEGIN_MACRO \
(_e)->next = (_l); \
(_e)->prev = (_l)->prev; \
(_l)->prev->next = (_e); \
(_l)->prev = (_e); \
ST_END_MACRO
/* Insert element "_e" into the list, after "_l" */
#define ST_INSERT_AFTER(_e,_l) \
ST_BEGIN_MACRO \
(_e)->next = (_l)->next; \
(_e)->prev = (_l); \
(_l)->next->prev = (_e); \
(_l)->next = (_e); \
ST_END_MACRO
/* Return the element following element "_e" */
#define ST_NEXT_LINK(_e) ((_e)->next)
/* Append an element "_e" to the end of the list "_l" */
#define ST_APPEND_LINK(_e,_l) ST_INSERT_BEFORE(_e,_l)
/* Insert an element "_e" at the head of the list "_l" */
#define ST_INSERT_LINK(_e,_l) ST_INSERT_AFTER(_e,_l)
/* Return the head/tail of the list */
#define ST_LIST_HEAD(_l) (_l)->next
#define ST_LIST_TAIL(_l) (_l)->prev
/* Remove the element "_e" from it's circular list */
#define ST_REMOVE_LINK(_e) \
ST_BEGIN_MACRO \
(_e)->prev->next = (_e)->next; \
(_e)->next->prev = (_e)->prev; \
ST_END_MACRO
/* Return non-zero if the given circular list "_l" is empty, */
/* zero if the circular list is not empty */
#define ST_CLIST_IS_EMPTY(_l) \
((_l)->next == (_l))
/* Initialize a circular list */
#define ST_INIT_CLIST(_l) \
ST_BEGIN_MACRO \
(_l)->next = (_l); \
(_l)->prev = (_l); \
ST_END_MACRO
#define ST_INIT_STATIC_CLIST(_l) \
{(_l), (_l)}
/*****************************************
* Basic types definitions
*/
typedef void (*_st_destructor_t)(void *);
typedef struct _st_stack {
_st_clist_t links;
char *vaddr; /* Base of stack's allocated memory */
int vaddr_size; /* Size of stack's allocated memory */
int stk_size; /* Size of usable portion of the stack */
char *stk_bottom; /* Lowest address of stack's usable portion */
char *stk_top; /* Highest address of stack's usable portion */
void *sp; /* Stack pointer from C's point of view */
} _st_stack_t;
typedef struct _st_cond {
_st_clist_t wait_q; /* Condition variable wait queue */
} _st_cond_t;
typedef struct _st_thread _st_thread_t;
struct _st_thread {
int state; /* Thread's state */
int flags; /* Thread's flags */
void *(*start)(void *arg); /* The start function of the thread */
void *arg; /* Argument of the start function */
void *retval; /* Return value of the start function */
_st_stack_t *stack; /* Info about thread's stack */
_st_clist_t links; /* For putting on run/sleep/zombie queue */
_st_clist_t wait_links; /* For putting on mutex/condvar wait queue */
#ifdef DEBUG
_st_clist_t tlink; /* For putting on thread queue */
#endif
st_utime_t due; /* Wakeup time when thread is sleeping */
_st_thread_t *left; /* For putting in timeout heap */
_st_thread_t *right; /* -- see docs/timeout_heap.txt for details */
int heap_index;
void **private_data; /* Per thread private data */
_st_cond_t *term; /* Termination condition variable for join */
jmp_buf context; /* Thread's context */
};
typedef struct _st_mutex {
_st_thread_t *owner; /* Current mutex owner */
_st_clist_t wait_q; /* Mutex wait queue */
} _st_mutex_t;
typedef struct _st_pollq {
_st_clist_t links; /* For putting on io queue */
_st_thread_t *thread; /* Polling thread */
struct pollfd *pds; /* Array of poll descriptors */
int npds; /* Length of the array */
int on_ioq; /* Is it on ioq? */
} _st_pollq_t;
typedef struct _st_eventsys_ops {
const char *name; /* Name of this event system */
int val; /* Type of this event system */
int (*init)(void); /* Initialization */
void (*dispatch)(void); /* Dispatch function */
int (*pollset_add)(struct pollfd *, int); /* Add descriptor set */
void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */
int (*fd_new)(int); /* New descriptor allocated */
int (*fd_close)(int); /* Descriptor closed */
int (*fd_getlimit)(void); /* Descriptor hard limit */
} _st_eventsys_t;
typedef struct _st_vp {
_st_thread_t *idle_thread; /* Idle thread for this vp */
st_utime_t last_clock; /* The last time we went into vp_check_clock() */
_st_clist_t run_q; /* run queue for this vp */
_st_clist_t io_q; /* io queue for this vp */
_st_clist_t zombie_q; /* zombie queue for this vp */
#ifdef DEBUG
_st_clist_t thread_q; /* all threads of this vp */
#endif
int pagesize;
_st_thread_t *sleep_q; /* sleep queue for this vp */
int sleepq_size; /* number of threads on sleep queue */
#ifdef ST_SWITCH_CB
st_switch_cb_t switch_out_cb; /* called when a thread is switched out */
st_switch_cb_t switch_in_cb; /* called when a thread is switched in */
#endif
} _st_vp_t;
typedef struct _st_netfd {
int osfd; /* Underlying OS file descriptor */
int inuse; /* In-use flag */
void *private_data; /* Per descriptor private data */
_st_destructor_t destructor; /* Private data destructor function */
void *aux_data; /* Auxiliary data for internal use */
struct _st_netfd *next; /* For putting on the free list */
} _st_netfd_t;
/*****************************************
* Current vp, thread, and event system
*/
extern _st_vp_t _st_this_vp;
extern _st_thread_t *_st_this_thread;
extern _st_eventsys_t *_st_eventsys;
#define _ST_CURRENT_THREAD() (_st_this_thread)
#define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread))
#define _ST_LAST_CLOCK (_st_this_vp.last_clock)
#define _ST_RUNQ (_st_this_vp.run_q)
#define _ST_IOQ (_st_this_vp.io_q)
#define _ST_ZOMBIEQ (_st_this_vp.zombie_q)
#ifdef DEBUG
#define _ST_THREADQ (_st_this_vp.thread_q)
#endif
#define _ST_PAGE_SIZE (_st_this_vp.pagesize)
#define _ST_SLEEPQ (_st_this_vp.sleep_q)
#define _ST_SLEEPQ_SIZE (_st_this_vp.sleepq_size)
#define _ST_VP_IDLE() (*_st_eventsys->dispatch)()
/*****************************************
* vp queues operations
*/
#define _ST_ADD_IOQ(_pq) ST_APPEND_LINK(&_pq.links, &_ST_IOQ)
#define _ST_DEL_IOQ(_pq) ST_REMOVE_LINK(&_pq.links)
#define _ST_ADD_RUNQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ)
#define _ST_DEL_RUNQ(_thr) ST_REMOVE_LINK(&(_thr)->links)
#define _ST_ADD_SLEEPQ(_thr, _timeout) _st_add_sleep_q(_thr, _timeout)
#define _ST_DEL_SLEEPQ(_thr) _st_del_sleep_q(_thr)
#define _ST_ADD_ZOMBIEQ(_thr) ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ)
#define _ST_DEL_ZOMBIEQ(_thr) ST_REMOVE_LINK(&(_thr)->links)
#ifdef DEBUG
#define _ST_ADD_THREADQ(_thr) ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ)
#define _ST_DEL_THREADQ(_thr) ST_REMOVE_LINK(&(_thr)->tlink)
#endif
/*****************************************
* Thread states and flags
*/
#define _ST_ST_RUNNING 0
#define _ST_ST_RUNNABLE 1
#define _ST_ST_IO_WAIT 2
#define _ST_ST_LOCK_WAIT 3
#define _ST_ST_COND_WAIT 4
#define _ST_ST_SLEEPING 5
#define _ST_ST_ZOMBIE 6
#define _ST_ST_SUSPENDED 7
#define _ST_FL_PRIMORDIAL 0x01
#define _ST_FL_IDLE_THREAD 0x02
#define _ST_FL_ON_SLEEPQ 0x04
#define _ST_FL_INTERRUPT 0x08
#define _ST_FL_TIMEDOUT 0x10
/*****************************************
* Pointer conversion
*/
#ifndef offsetof
#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier))
#endif
#define _ST_THREAD_PTR(_qp) \
((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, links)))
#define _ST_THREAD_WAITQ_PTR(_qp) \
((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, wait_links)))
#define _ST_THREAD_STACK_PTR(_qp) \
((_st_stack_t *)((char*)(_qp) - offsetof(_st_stack_t, links)))
#define _ST_POLLQUEUE_PTR(_qp) \
((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links)))
#ifdef DEBUG
#define _ST_THREAD_THREADQ_PTR(_qp) \
((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink)))
#endif
/*****************************************
* Constants
*/
#ifndef ST_UTIME_NO_TIMEOUT
#define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL)
#endif
#define ST_DEFAULT_STACK_SIZE (64*1024)
#ifndef ST_KEYS_MAX
#define ST_KEYS_MAX 16
#endif
#ifndef ST_MIN_POLLFDS_SIZE
#define ST_MIN_POLLFDS_SIZE 64
#endif
/*****************************************
* Threads context switching
*/
#ifdef DEBUG
void _st_iterate_threads(void);
#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads()
#else
#define ST_DEBUG_ITERATE_THREADS()
#endif
#ifdef ST_SWITCH_CB
#define ST_SWITCH_OUT_CB(_thread) \
if (_st_this_vp.switch_out_cb != NULL && \
_thread != _st_this_vp.idle_thread && \
_thread->state != _ST_ST_ZOMBIE) { \
_st_this_vp.switch_out_cb(); \
}
#define ST_SWITCH_IN_CB(_thread) \
if (_st_this_vp.switch_in_cb != NULL && \
_thread != _st_this_vp.idle_thread && \
_thread->state != _ST_ST_ZOMBIE) { \
_st_this_vp.switch_in_cb(); \
}
#else
#define ST_SWITCH_OUT_CB(_thread)
#define ST_SWITCH_IN_CB(_thread)
#endif
/*
* Switch away from the current thread context by saving its state and
* calling the thread scheduler
*/
#define _ST_SWITCH_CONTEXT(_thread) \
ST_BEGIN_MACRO \
ST_SWITCH_OUT_CB(_thread); \
if (!MD_SETJMP((_thread)->context)) { \
_st_vp_schedule(); \
} \
ST_DEBUG_ITERATE_THREADS(); \
ST_SWITCH_IN_CB(_thread); \
ST_END_MACRO
/*
* Restore a thread context that was saved by _ST_SWITCH_CONTEXT or
* initialized by _ST_INIT_CONTEXT
*/
#define _ST_RESTORE_CONTEXT(_thread) \
ST_BEGIN_MACRO \
_ST_SET_CURRENT_THREAD(_thread); \
MD_LONGJMP((_thread)->context, 1); \
ST_END_MACRO
/*
* Number of bytes reserved under the stack "bottom"
*/
#define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE
/*****************************************
* Forward declarations
*/
void _st_vp_schedule(void);
void _st_vp_check_clock(void);
void *_st_idle_thread_start(void *arg);
void _st_thread_main(void);
void _st_thread_cleanup(_st_thread_t *thread);
void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout);
void _st_del_sleep_q(_st_thread_t *thread);
_st_stack_t *_st_stack_new(int stack_size);
void _st_stack_free(_st_stack_t *ts);
int _st_io_init(void);
st_utime_t st_utime(void);
_st_cond_t *st_cond_new(void);
int st_cond_destroy(_st_cond_t *cvar);
int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout);
int st_cond_signal(_st_cond_t *cvar);
ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout);
ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout);
int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size);
#endif /* !__ST_COMMON_H__ */

@ -1,483 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
* Yahoo! Inc.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "common.h"
#ifdef USE_POLL
#error "Not support USE_POLL"
#endif
#ifdef MD_HAVE_KQUEUE
#error "Not support MD_HAVE_KQUEUE"
#endif
#ifdef MD_HAVE_POLL
#error "Not support MD_HAVE_POLL"
#endif
#ifndef MD_HAVE_EPOLL
#error "Only support MD_HAVE_EPOLL"
#endif
#include <sys/epoll.h>
typedef struct _epoll_fd_data {
int rd_ref_cnt;
int wr_ref_cnt;
int ex_ref_cnt;
int revents;
} _epoll_fd_data_t;
static struct _st_epolldata {
_epoll_fd_data_t *fd_data;
struct epoll_event *evtlist;
int fd_data_size;
int evtlist_size;
int evtlist_cnt;
int fd_hint;
int epfd;
pid_t pid;
} *_st_epoll_data;
#ifndef ST_EPOLL_EVTLIST_SIZE
/* Not a limit, just a hint */
#define ST_EPOLL_EVTLIST_SIZE 4096
#endif
#define _ST_EPOLL_READ_CNT(fd) (_st_epoll_data->fd_data[fd].rd_ref_cnt)
#define _ST_EPOLL_WRITE_CNT(fd) (_st_epoll_data->fd_data[fd].wr_ref_cnt)
#define _ST_EPOLL_EXCEP_CNT(fd) (_st_epoll_data->fd_data[fd].ex_ref_cnt)
#define _ST_EPOLL_REVENTS(fd) (_st_epoll_data->fd_data[fd].revents)
#define _ST_EPOLL_READ_BIT(fd) (_ST_EPOLL_READ_CNT(fd) ? EPOLLIN : 0)
#define _ST_EPOLL_WRITE_BIT(fd) (_ST_EPOLL_WRITE_CNT(fd) ? EPOLLOUT : 0)
#define _ST_EPOLL_EXCEP_BIT(fd) (_ST_EPOLL_EXCEP_CNT(fd) ? EPOLLPRI : 0)
#define _ST_EPOLL_EVENTS(fd) \
(_ST_EPOLL_READ_BIT(fd)|_ST_EPOLL_WRITE_BIT(fd)|_ST_EPOLL_EXCEP_BIT(fd))
_st_eventsys_t *_st_eventsys = NULL;
/*****************************************
* epoll event system
*/
ST_HIDDEN int _st_epoll_init(void)
{
int fdlim;
int err = 0;
int rv = 0;
_st_epoll_data = (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data));
if (!_st_epoll_data) {
return -1;
}
fdlim = st_getfdlimit();
_st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? fdlim : ST_EPOLL_EVTLIST_SIZE;
if ((_st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint)) < 0) {
err = errno;
rv = -1;
goto cleanup_epoll;
}
fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC);
_st_epoll_data->pid = getpid();
/* Allocate file descriptor data array */
_st_epoll_data->fd_data_size = _st_epoll_data->fd_hint;
_st_epoll_data->fd_data = (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, sizeof(_epoll_fd_data_t));
if (!_st_epoll_data->fd_data) {
err = errno;
rv = -1;
goto cleanup_epoll;
}
/* Allocate event lists */
_st_epoll_data->evtlist_size = _st_epoll_data->fd_hint;
_st_epoll_data->evtlist = (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * sizeof(struct epoll_event));
if (!_st_epoll_data->evtlist) {
err = errno;
rv = -1;
}
cleanup_epoll:
if (rv < 0) {
if (_st_epoll_data->epfd >= 0) {
close(_st_epoll_data->epfd);
}
free(_st_epoll_data->fd_data);
free(_st_epoll_data->evtlist);
free(_st_epoll_data);
_st_epoll_data = NULL;
errno = err;
}
return rv;
}
ST_HIDDEN int _st_epoll_fd_data_expand(int maxfd)
{
_epoll_fd_data_t *ptr;
int n = _st_epoll_data->fd_data_size;
while (maxfd >= n) {
n <<= 1;
}
ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, n * sizeof(_epoll_fd_data_t));
if (!ptr) {
return -1;
}
memset(ptr + _st_epoll_data->fd_data_size, 0, (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t));
_st_epoll_data->fd_data = ptr;
_st_epoll_data->fd_data_size = n;
return 0;
}
ST_HIDDEN void _st_epoll_evtlist_expand(void)
{
struct epoll_event *ptr;
int n = _st_epoll_data->evtlist_size;
while (_st_epoll_data->evtlist_cnt > n) {
n <<= 1;
}
ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, n * sizeof(struct epoll_event));
if (ptr) {
_st_epoll_data->evtlist = ptr;
_st_epoll_data->evtlist_size = n;
}
}
ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds)
{
struct epoll_event ev;
struct pollfd *pd;
struct pollfd *epd = pds + npds;
int old_events, events, op;
/*
* It's more or less OK if deleting fails because a descriptor
* will either be closed or deleted in dispatch function after
* it fires.
*/
for (pd = pds; pd < epd; pd++) {
old_events = _ST_EPOLL_EVENTS(pd->fd);
if (pd->events & POLLIN) {
_ST_EPOLL_READ_CNT(pd->fd)--;
}
if (pd->events & POLLOUT) {
_ST_EPOLL_WRITE_CNT(pd->fd)--;
}
if (pd->events & POLLPRI) {
_ST_EPOLL_EXCEP_CNT(pd->fd)--;
}
events = _ST_EPOLL_EVENTS(pd->fd);
/*
* The _ST_EPOLL_REVENTS check below is needed so we can use
* this function inside dispatch(). Outside of dispatch()
* _ST_EPOLL_REVENTS is always zero for all descriptors.
*/
if (events != old_events && _ST_EPOLL_REVENTS(pd->fd) == 0) {
op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
ev.events = events;
ev.data.fd = pd->fd;
if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && op == EPOLL_CTL_DEL) {
_st_epoll_data->evtlist_cnt--;
}
}
}
}
ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds)
{
struct epoll_event ev;
int i, fd;
int old_events, events, op;
/* Do as many checks as possible up front */
for (i = 0; i < npds; i++) {
fd = pds[i].fd;
if (fd < 0 || !pds[i].events || (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) {
errno = EINVAL;
return -1;
}
if (fd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(fd) < 0) {
return -1;
}
}
for (i = 0; i < npds; i++) {
fd = pds[i].fd;
old_events = _ST_EPOLL_EVENTS(fd);
if (pds[i].events & POLLIN) {
_ST_EPOLL_READ_CNT(fd)++;
}
if (pds[i].events & POLLOUT) {
_ST_EPOLL_WRITE_CNT(fd)++;
}
if (pds[i].events & POLLPRI) {
_ST_EPOLL_EXCEP_CNT(fd)++;
}
events = _ST_EPOLL_EVENTS(fd);
if (events != old_events) {
op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
ev.events = events;
ev.data.fd = fd;
if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && (op != EPOLL_CTL_ADD || errno != EEXIST)) {
break;
}
if (op == EPOLL_CTL_ADD) {
_st_epoll_data->evtlist_cnt++;
if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) {
_st_epoll_evtlist_expand();
}
}
}
}
if (i < npds) {
/* Error */
int err = errno;
/* Unroll the state */
_st_epoll_pollset_del(pds, i + 1);
errno = err;
return -1;
}
return 0;
}
ST_HIDDEN void _st_epoll_dispatch(void)
{
st_utime_t min_timeout;
_st_clist_t *q;
_st_pollq_t *pq;
struct pollfd *pds, *epds;
struct epoll_event ev;
int timeout, nfd, i, osfd, notify;
int events, op;
short revents;
if (_ST_SLEEPQ == NULL) {
timeout = -1;
} else {
min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK);
timeout = (int) (min_timeout / 1000);
}
if (_st_epoll_data->pid != getpid()) {
// WINLIN: remove it for bug introduced.
// @see: https://github.com/ossrs/srs/issues/193
exit(-1);
}
/* Check for I/O operations */
nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, _st_epoll_data->evtlist_size, timeout);
if (nfd > 0) {
for (i = 0; i < nfd; i++) {
osfd = _st_epoll_data->evtlist[i].data.fd;
_ST_EPOLL_REVENTS(osfd) = _st_epoll_data->evtlist[i].events;
if (_ST_EPOLL_REVENTS(osfd) & (EPOLLERR | EPOLLHUP)) {
/* Also set I/O bits on error */
_ST_EPOLL_REVENTS(osfd) |= _ST_EPOLL_EVENTS(osfd);
}
}
for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) {
pq = _ST_POLLQUEUE_PTR(q);
notify = 0;
epds = pq->pds + pq->npds;
for (pds = pq->pds; pds < epds; pds++) {
if (_ST_EPOLL_REVENTS(pds->fd) == 0) {
pds->revents = 0;
continue;
}
osfd = pds->fd;
events = pds->events;
revents = 0;
if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) {
revents |= POLLIN;
}
if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) {
revents |= POLLOUT;
}
if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) {
revents |= POLLPRI;
}
if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) {
revents |= POLLERR;
}
if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) {
revents |= POLLHUP;
}
pds->revents = revents;
if (revents) {
notify = 1;
}
}
if (notify) {
ST_REMOVE_LINK(&pq->links);
pq->on_ioq = 0;
/*
* Here we will only delete/modify descriptors that
* didn't fire (see comments in _st_epoll_pollset_del()).
*/
_st_epoll_pollset_del(pq->pds, pq->npds);
if (pq->thread->flags & _ST_FL_ON_SLEEPQ) {
_ST_DEL_SLEEPQ(pq->thread);
}
pq->thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(pq->thread);
}
}
for (i = 0; i < nfd; i++) {
/* Delete/modify descriptors that fired */
osfd = _st_epoll_data->evtlist[i].data.fd;
_ST_EPOLL_REVENTS(osfd) = 0;
events = _ST_EPOLL_EVENTS(osfd);
op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
ev.events = events;
ev.data.fd = osfd;
if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && op == EPOLL_CTL_DEL) {
_st_epoll_data->evtlist_cnt--;
}
}
}
}
ST_HIDDEN int _st_epoll_fd_new(int osfd)
{
if (osfd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(osfd) < 0) {
return -1;
}
return 0;
}
ST_HIDDEN int _st_epoll_fd_close(int osfd)
{
if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || _ST_EPOLL_EXCEP_CNT(osfd)) {
errno = EBUSY;
return -1;
}
return 0;
}
ST_HIDDEN int _st_epoll_fd_getlimit(void)
{
/* zero means no specific limit */
return 0;
}
/*
* Check if epoll functions are just stubs.
*/
ST_HIDDEN int _st_epoll_is_supported(void)
{
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = NULL;
/* Guaranteed to fail */
epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev);
return (errno != ENOSYS);
}
static _st_eventsys_t _st_epoll_eventsys = {
"epoll",
ST_EVENTSYS_ALT,
_st_epoll_init,
_st_epoll_dispatch,
_st_epoll_pollset_add,
_st_epoll_pollset_del,
_st_epoll_fd_new,
_st_epoll_fd_close,
_st_epoll_fd_getlimit
};
/*****************************************
* Public functions
*/
int st_set_eventsys(int eventsys)
{
if (_st_eventsys) {
errno = EBUSY;
return -1;
}
switch (eventsys) {
case ST_EVENTSYS_DEFAULT:
case ST_EVENTSYS_ALT:
default:
if (_st_epoll_is_supported()) {
_st_eventsys = &_st_epoll_eventsys;
break;
}
errno = EINVAL;
return -1;
}
return 0;
}
int st_get_eventsys(void)
{
return _st_eventsys ? _st_eventsys->val : -1;
}
const char *st_get_eventsys_name(void)
{
return _st_eventsys ? _st_eventsys->name : "";
}

@ -1,792 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
*
* Portions created by SGI are Copyright (C) 2000-2001 Silicon
* Graphics, Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* This file is derived directly from Netscape Communications Corporation,
* and consists of extensive modifications made during the year(s) 1999-2000.
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include "common.h"
#if EAGAIN != EWOULDBLOCK
#define _IO_NOT_READY_ERROR ((errno == EAGAIN) || (errno == EWOULDBLOCK))
#else
#define _IO_NOT_READY_ERROR (errno == EAGAIN)
#endif
#define _LOCAL_MAXIOV 16
/* File descriptor object free list */
static _st_netfd_t *_st_netfd_freelist = NULL;
/* Maximum number of file descriptors that the process can open */
static int _st_osfd_limit = -1;
static void _st_netfd_free_aux_data(_st_netfd_t *fd);
int _st_io_init(void)
{
struct sigaction sigact;
struct rlimit rlim;
int fdlim;
/* Ignore SIGPIPE */
sigact.sa_handler = SIG_IGN;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
if (sigaction(SIGPIPE, &sigact, NULL) < 0) {
return -1;
}
/* Set maximum number of open file descriptors */
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
return -1;
}
fdlim = (*_st_eventsys->fd_getlimit)();
if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) {
rlim.rlim_max = fdlim;
}
rlim.rlim_cur = rlim.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
return -1;
}
_st_osfd_limit = (int) rlim.rlim_max;
return 0;
}
int st_getfdlimit(void)
{
return _st_osfd_limit;
}
void st_netfd_free(_st_netfd_t *fd)
{
if (!fd->inuse) {
return;
}
fd->inuse = 0;
if (fd->aux_data) {
_st_netfd_free_aux_data(fd);
}
if (fd->private_data && fd->destructor) {
(*(fd->destructor))(fd->private_data);
}
fd->private_data = NULL;
fd->destructor = NULL;
fd->next = _st_netfd_freelist;
_st_netfd_freelist = fd;
}
static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket)
{
_st_netfd_t *fd;
int flags = 1;
if ((*_st_eventsys->fd_new)(osfd) < 0) {
return NULL;
}
if (_st_netfd_freelist) {
fd = _st_netfd_freelist;
_st_netfd_freelist = _st_netfd_freelist->next;
} else {
fd = calloc(1, sizeof(_st_netfd_t));
if (!fd) {
return NULL;
}
}
fd->osfd = osfd;
fd->inuse = 1;
fd->next = NULL;
if (nonblock) {
/* Use just one system call */
if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) {
return fd;
}
/* Do it the Posix way */
if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) {
st_netfd_free(fd);
return NULL;
}
}
return fd;
}
_st_netfd_t *st_netfd_open(int osfd)
{
return _st_netfd_new(osfd, 1, 0);
}
_st_netfd_t *st_netfd_open_socket(int osfd)
{
return _st_netfd_new(osfd, 1, 1);
}
int st_netfd_close(_st_netfd_t *fd)
{
if ((*_st_eventsys->fd_close)(fd->osfd) < 0) {
return -1;
}
st_netfd_free(fd);
return close(fd->osfd);
}
int st_netfd_fileno(_st_netfd_t *fd)
{
return (fd->osfd);
}
void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destructor)
{
if (value != fd->private_data) {
/* Free up previously set non-NULL data value */
if (fd->private_data && fd->destructor) {
(*(fd->destructor))(fd->private_data);
}
}
fd->private_data = value;
fd->destructor = destructor;
}
void *st_netfd_getspecific(_st_netfd_t *fd)
{
return (fd->private_data);
}
/*
* Wait for I/O on a single descriptor.
*/
int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout)
{
struct pollfd pd;
int n;
pd.fd = fd->osfd;
pd.events = (short) how;
pd.revents = 0;
if ((n = st_poll(&pd, 1, timeout)) < 0) {
return -1;
}
if (n == 0) {
/* Timed out */
errno = ETIME;
return -1;
}
if (pd.revents & POLLNVAL) {
errno = EBADF;
return -1;
}
return 0;
}
#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT
/* No-op */
int st_netfd_serialize_accept(_st_netfd_t *fd)
{
fd->aux_data = NULL;
return 0;
}
/* No-op */
static void _st_netfd_free_aux_data(_st_netfd_t *fd)
{
fd->aux_data = NULL;
}
_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout)
{
int osfd, err;
_st_netfd_t *newfd;
while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return NULL;
}
/* Wait until the socket becomes readable */
if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
return NULL;
}
}
/* On some platforms the new socket created by accept() inherits */
/* the nonblocking attribute of the listening socket */
#if defined (MD_ACCEPT_NB_INHERITED)
newfd = _st_netfd_new(osfd, 0, 1);
#elif defined (MD_ACCEPT_NB_NOT_INHERITED)
newfd = _st_netfd_new(osfd, 1, 1);
#else
#error Unknown OS
#endif
if (!newfd) {
err = errno;
close(osfd);
errno = err;
}
return newfd;
}
#else /* MD_ALWAYS_UNSERIALIZED_ACCEPT */
/*
* On some platforms accept() calls from different processes
* on the same listen socket must be serialized.
* The following code serializes accept()'s without process blocking.
* A pipe is used as an inter-process semaphore.
*/
int st_netfd_serialize_accept(_st_netfd_t *fd)
{
_st_netfd_t **p;
int osfd[2], err;
if (fd->aux_data) {
errno = EINVAL;
return -1;
}
if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) {
return -1;
}
if (pipe(osfd) < 0) {
free(p);
return -1;
}
if ((p[0] = st_netfd_open(osfd[0])) != NULL && (p[1] = st_netfd_open(osfd[1])) != NULL && write(osfd[1], " ", 1) == 1) {
fd->aux_data = p;
return 0;
}
/* Error */
err = errno;
if (p[0]) {
st_netfd_free(p[0]);
}
if (p[1]) {
st_netfd_free(p[1]);
}
close(osfd[0]);
close(osfd[1]);
free(p);
errno = err;
return -1;
}
static void _st_netfd_free_aux_data(_st_netfd_t *fd)
{
_st_netfd_t **p = (_st_netfd_t **) fd->aux_data;
st_netfd_close(p[0]);
st_netfd_close(p[1]);
free(p);
fd->aux_data = NULL;
}
_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout)
{
int osfd, err;
_st_netfd_t *newfd;
_st_netfd_t **p = (_st_netfd_t **) fd->aux_data;
ssize_t n;
char c;
for ( ; ; ) {
if (p == NULL) {
osfd = accept(fd->osfd, addr, (socklen_t *)addrlen);
} else {
/* Get the lock */
n = st_read(p[0], &c, 1, timeout);
if (n < 0) {
return NULL;
}
ST_ASSERT(n == 1);
/* Got the lock */
osfd = accept(fd->osfd, addr, (socklen_t *)addrlen);
/* Unlock */
err = errno;
n = st_write(p[1], &c, 1, timeout);
ST_ASSERT(n == 1);
errno = err;
}
if (osfd >= 0) {
break;
}
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return NULL;
}
/* Wait until the socket becomes readable */
if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
return NULL;
}
}
/* On some platforms the new socket created by accept() inherits */
/* the nonblocking attribute of the listening socket */
#if defined (MD_ACCEPT_NB_INHERITED)
newfd = _st_netfd_new(osfd, 0, 1);
#elif defined (MD_ACCEPT_NB_NOT_INHERITED)
newfd = _st_netfd_new(osfd, 1, 1);
#else
#error Unknown OS
#endif
if (!newfd) {
err = errno;
close(osfd);
errno = err;
}
return newfd;
}
#endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */
int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout)
{
int n, err = 0;
while (connect(fd->osfd, addr, addrlen) < 0) {
if (errno != EINTR) {
/*
* On some platforms, if connect() is interrupted (errno == EINTR)
* after the kernel binds the socket, a subsequent connect()
* attempt will fail with errno == EADDRINUSE. Ignore EADDRINUSE
* iff connect() was previously interrupted. See Rich Stevens'
* "UNIX Network Programming," Vol. 1, 2nd edition, p. 413
* ("Interrupted connect").
*/
if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) {
return -1;
}
/* Wait until the socket becomes writable */
if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
return -1;
}
/* Try to find out whether the connection setup succeeded or failed */
n = sizeof(int);
if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, (socklen_t *)&n) < 0) {
return -1;
}
if (err) {
errno = err;
return -1;
}
break;
}
err = 1;
}
return 0;
}
ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout)
{
ssize_t n;
while ((n = read(fd->osfd, buf, nbyte)) < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return -1;
}
/* Wait until the socket becomes readable */
if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
return -1;
}
}
return n;
}
int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout)
{
struct iovec iov, *riov;
int riov_size, rv;
iov.iov_base = buf;
iov.iov_len = *resid;
riov = &iov;
riov_size = 1;
rv = st_readv_resid(fd, &riov, &riov_size, timeout);
*resid = iov.iov_len;
return rv;
}
ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout)
{
ssize_t n;
while ((n = readv(fd->osfd, iov, iov_size)) < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return -1;
}
/* Wait until the socket becomes readable */
if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
return -1;
}
}
return n;
}
int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout)
{
ssize_t n;
while (*iov_size > 0) {
if (*iov_size == 1) {
n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len);
} else {
n = readv(fd->osfd, *iov, *iov_size);
}
if (n < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return -1;
}
} else if (n == 0) {
break;
} else {
while ((size_t) n >= (*iov)->iov_len) {
n -= (*iov)->iov_len;
(*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len;
(*iov)->iov_len = 0;
(*iov)++;
(*iov_size)--;
if (n == 0) {
break;
}
}
if (*iov_size == 0) {
break;
}
(*iov)->iov_base = (char *) (*iov)->iov_base + n;
(*iov)->iov_len -= n;
}
/* Wait until the socket becomes readable */
if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
return -1;
}
}
return 0;
}
ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout)
{
size_t resid = nbyte;
return st_read_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1;
}
int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, st_utime_t timeout)
{
struct iovec iov, *riov;
int riov_size, rv;
iov.iov_base = (void *) buf; /* we promise not to modify buf */
iov.iov_len = *resid;
riov = &iov;
riov_size = 1;
rv = st_writev_resid(fd, &riov, &riov_size, timeout);
*resid = iov.iov_len;
return rv;
}
ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout)
{
size_t resid = nbyte;
return st_write_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1;
}
ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout)
{
ssize_t n, rv;
size_t nleft, nbyte;
int index, iov_cnt;
struct iovec *tmp_iov;
struct iovec local_iov[_LOCAL_MAXIOV];
/* Calculate the total number of bytes to be sent */
nbyte = 0;
for (index = 0; index < iov_size; index++) {
nbyte += iov[index].iov_len;
}
rv = (ssize_t)nbyte;
nleft = nbyte;
tmp_iov = (struct iovec *) iov; /* we promise not to modify iov */
iov_cnt = iov_size;
while (nleft > 0) {
if (iov_cnt == 1) {
if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) {
rv = -1;
}
break;
}
if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
rv = -1;
break;
}
} else {
if ((size_t) n == nleft) {
break;
}
nleft -= n;
/* Find the next unwritten vector */
n = (ssize_t)(nbyte - nleft);
for (index = 0; (size_t) n >= iov[index].iov_len; index++) {
n -= iov[index].iov_len;
}
if (tmp_iov == iov) {
/* Must copy iov's around */
if (iov_size - index <= _LOCAL_MAXIOV) {
tmp_iov = local_iov;
} else {
tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec));
if (tmp_iov == NULL) {
return -1;
}
}
}
/* Fill in the first partial read */
tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]);
tmp_iov[0].iov_len = iov[index].iov_len - n;
index++;
/* Copy the remaining vectors */
for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) {
tmp_iov[iov_cnt].iov_base = iov[index].iov_base;
tmp_iov[iov_cnt].iov_len = iov[index].iov_len;
}
}
/* Wait until the socket becomes writable */
if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
rv = -1;
break;
}
}
if (tmp_iov != iov && tmp_iov != local_iov) {
free(tmp_iov);
}
return rv;
}
int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout)
{
ssize_t n;
while (*iov_size > 0) {
if (*iov_size == 1) {
n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len);
} else {
n = writev(fd->osfd, *iov, *iov_size);
}
if (n < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return -1;
}
} else {
while ((size_t) n >= (*iov)->iov_len) {
n -= (*iov)->iov_len;
(*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len;
(*iov)->iov_len = 0;
(*iov)++;
(*iov_size)--;
if (n == 0) {
break;
}
}
if (*iov_size == 0) {
break;
}
(*iov)->iov_base = (char *) (*iov)->iov_base + n;
(*iov)->iov_len -= n;
}
/* Wait until the socket becomes writable */
if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
return -1;
}
}
return 0;
}
/*
* Simple I/O functions for UDP.
*/
int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout)
{
int n;
while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return -1;
}
/* Wait until the socket becomes readable */
if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
return -1;
}
}
return n;
}
int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout)
{
int n;
while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return -1;
}
/* Wait until the socket becomes writable */
if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
return -1;
}
}
return n;
}
int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout)
{
int n;
while ((n = recvmsg(fd->osfd, msg, flags)) < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return -1;
}
/* Wait until the socket becomes readable */
if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
return -1;
}
}
return n;
}
int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t timeout)
{
int n;
while ((n = sendmsg(fd->osfd, msg, flags)) < 0) {
if (errno == EINTR) {
continue;
}
if (!_IO_NOT_READY_ERROR) {
return -1;
}
/* Wait until the socket becomes writable */
if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
return -1;
}
}
return n;
}
/*
* To open FIFOs or other special files.
*/
_st_netfd_t *st_open(const char *path, int oflags, mode_t mode)
{
int osfd, err;
_st_netfd_t *newfd;
while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) {
if (errno != EINTR) {
return NULL;
}
}
newfd = _st_netfd_new(osfd, 0, 0);
if (!newfd) {
err = errno;
close(osfd);
errno = err;
}
return newfd;
}

@ -1,116 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
*
* Portions created by SGI are Copyright (C) 2000-2001 Silicon
* Graphics, Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* This file is derived directly from Netscape Communications Corporation,
* and consists of extensive modifications made during the year(s) 1999-2000.
*/
#include <stdlib.h>
#include <errno.h>
#include "common.h"
/*
* Destructor table for per-thread private data
*/
static _st_destructor_t _st_destructors[ST_KEYS_MAX];
static int key_max = 0;
/*
* Return a key to be used for thread specific data
*/
int st_key_create(int *keyp, _st_destructor_t destructor)
{
if (key_max >= ST_KEYS_MAX) {
errno = EAGAIN;
return -1;
}
*keyp = key_max++;
_st_destructors[*keyp] = destructor;
return 0;
}
int st_key_getlimit(void)
{
return ST_KEYS_MAX;
}
int st_thread_setspecific(int key, void *value)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
if (key < 0 || key >= key_max) {
errno = EINVAL;
return -1;
}
if (value != me->private_data[key]) {
/* free up previously set non-NULL data value */
if (me->private_data[key] && _st_destructors[key]) {
(*_st_destructors[key])(me->private_data[key]);
}
me->private_data[key] = value;
}
return 0;
}
void *st_thread_getspecific(int key)
{
if (key < 0 || key >= key_max) {
return NULL;
}
return ((_ST_CURRENT_THREAD())->private_data[key]);
}
/*
* Free up all per-thread private data
*/
void _st_thread_cleanup(_st_thread_t *thread)
{
int key;
for (key = 0; key < key_max; key++) {
if (thread->private_data[key] && _st_destructors[key]) {
(*_st_destructors[key])(thread->private_data[key]);
thread->private_data[key] = NULL;
}
}
}

@ -1,151 +0,0 @@
/*
* Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
* All Rights Reserved.
*/
/****************************************************************/
#if defined(__i386__)
/*
* Internal __jmp_buf layout
*/
#define JB_BX 0
#define JB_SI 1
#define JB_DI 2
#define JB_BP 3
#define JB_SP 4
#define JB_PC 5
.file "md.S"
.text
/* _st_md_cxt_save(__jmp_buf env) */
.globl _st_md_cxt_save
.type _st_md_cxt_save, @function
.align 16
_st_md_cxt_save:
movl 4(%esp), %eax
/*
* Save registers.
*/
movl %ebx, (JB_BX*4)(%eax)
movl %esi, (JB_SI*4)(%eax)
movl %edi, (JB_DI*4)(%eax)
/* Save SP */
leal 4(%esp), %ecx
movl %ecx, (JB_SP*4)(%eax)
/* Save PC we are returning to */
movl 0(%esp), %ecx
movl %ecx, (JB_PC*4)(%eax)
/* Save caller frame pointer */
movl %ebp, (JB_BP*4)(%eax)
xorl %eax, %eax
ret
.size _st_md_cxt_save, .-_st_md_cxt_save
/****************************************************************/
/* _st_md_cxt_restore(__jmp_buf env, int val) */
.globl _st_md_cxt_restore
.type _st_md_cxt_restore, @function
.align 16
_st_md_cxt_restore:
/* First argument is jmp_buf */
movl 4(%esp), %ecx
/* Second argument is return value */
movl 8(%esp), %eax
/* Set the return address */
movl (JB_PC*4)(%ecx), %edx
/*
* Restore registers.
*/
movl (JB_BX*4)(%ecx), %ebx
movl (JB_SI*4)(%ecx), %esi
movl (JB_DI*4)(%ecx), %edi
movl (JB_BP*4)(%ecx), %ebp
movl (JB_SP*4)(%ecx), %esp
testl %eax, %eax
jnz 1f
incl %eax
/* Jump to saved PC */
1: jmp *%edx
.size _st_md_cxt_restore, .-_st_md_cxt_restore
/****************************************************************/
#elif defined(__amd64__) || defined(__x86_64__)
/*
* Internal __jmp_buf layout
*/
#define JB_RBX 0
#define JB_RBP 1
#define JB_R12 2
#define JB_R13 3
#define JB_R14 4
#define JB_R15 5
#define JB_RSP 6
#define JB_PC 7
.file "md.S"
.text
/* _st_md_cxt_save(__jmp_buf env) */
.globl _st_md_cxt_save
.type _st_md_cxt_save, @function
.align 16
_st_md_cxt_save:
/*
* Save registers.
*/
movq %rbx, (JB_RBX*8)(%rdi)
movq %rbp, (JB_RBP*8)(%rdi)
movq %r12, (JB_R12*8)(%rdi)
movq %r13, (JB_R13*8)(%rdi)
movq %r14, (JB_R14*8)(%rdi)
movq %r15, (JB_R15*8)(%rdi)
/* Save SP */
leaq 8(%rsp), %rdx
movq %rdx, (JB_RSP*8)(%rdi)
/* Save PC we are returning to */
movq (%rsp), %rax
movq %rax, (JB_PC*8)(%rdi)
xorq %rax, %rax
ret
.size _st_md_cxt_save, .-_st_md_cxt_save
/****************************************************************/
/* _st_md_cxt_restore(__jmp_buf env, int val) */
.globl _st_md_cxt_restore
.type _st_md_cxt_restore, @function
.align 16
_st_md_cxt_restore:
/*
* Restore registers.
*/
movq (JB_RBX*8)(%rdi), %rbx
movq (JB_RBP*8)(%rdi), %rbp
movq (JB_R12*8)(%rdi), %r12
movq (JB_R13*8)(%rdi), %r13
movq (JB_R14*8)(%rdi), %r14
movq (JB_R15*8)(%rdi), %r15
/* Set return value */
test %esi, %esi
mov $01, %eax
cmove %eax, %esi
mov %esi, %eax
movq (JB_PC*8)(%rdi), %rdx
movq (JB_RSP*8)(%rdi), %rsp
/* Jump to saved PC */
jmpq *%rdx
.size _st_md_cxt_restore, .-_st_md_cxt_restore
/****************************************************************/
#endif

@ -1,193 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
*
* Portions created by SGI are Copyright (C) 2000-2001 Silicon
* Graphics, Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* This file is derived directly from Netscape Communications Corporation,
* and consists of extensive modifications made during the year(s) 1999-2000.
*/
#ifndef __ST_MD_H__
#define __ST_MD_H__
#if defined(ETIMEDOUT) && !defined(ETIME)
#define ETIME ETIMEDOUT
#endif
#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
#define MAP_ANON MAP_ANONYMOUS
#endif
#ifndef MAP_FAILED
#define MAP_FAILED -1
#endif
/*****************************************
* Platform specifics
*/
#if defined (LINUX)
/* linux ok, defined bellow */
#elif defined (AIX)
#error "AIX not supported"
#elif defined (CYGWIN)
#error "CYGWIN not supported"
#elif defined (DARWIN)
#error "DARWIN not supported"
#elif defined (FREEBSD)
#error "FREEBSD not supported"
#elif defined (HPUX)
#error "HPUX not supported"
#elif defined (IRIX)
#error "IRIX not supported"
#elif defined (NETBSD)
#error "NETBSD not supported"
#elif defined (OPENBSD)
#error "OPENBSD not supported"
#elif defined (OSF1)
#error "OSF1 not supported"
#elif defined (SOLARIS)
#error "SOLARIS not supported"
#else
#error "Unknown OS"
#endif /* OS */
/* linux only, defined bellow */
/*
* These are properties of the linux kernel and are the same on every
* flavor and architecture.
*/
#define MD_USE_BSD_ANON_MMAP
#define MD_ACCEPT_NB_NOT_INHERITED
#define MD_ALWAYS_UNSERIALIZED_ACCEPT
/*
* Modern GNU/Linux is Posix.1g compliant.
*/
#define MD_HAVE_SOCKLEN_T
/*
* All architectures and flavors of linux have the gettimeofday
* function but if you know of a faster way, use it.
*/
#define MD_GET_UTIME() \
struct timeval tv; \
(void) gettimeofday(&tv, NULL); \
return (tv.tv_sec * 1000000LL + tv.tv_usec)
#if defined(__mips__)
#define MD_STACK_GROWS_DOWN
#else /* Not or mips */
/*
* On linux, there are a few styles of jmpbuf format. These vary based
* on architecture/glibc combination.
*
* Most of the glibc based toggles were lifted from:
* mozilla/nsprpub/pr/include/md/_linux.h
*/
/*
* Starting with glibc 2.4, JB_SP definitions are not public anymore.
* They, however, can still be found in glibc source tree in
* architecture-specific "jmpbuf-offsets.h" files.
* Most importantly, the content of jmp_buf is mangled by setjmp to make
* it completely opaque (the mangling can be disabled by setting the
* LD_POINTER_GUARD environment variable before application execution).
* Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore
* functions as a setjmp/longjmp replacement wherever they are available
* unless USE_LIBC_SETJMP is defined.
*/
#if defined(__i386__)
#define MD_STACK_GROWS_DOWN
#define MD_USE_BUILTIN_SETJMP
#if defined(__GLIBC__) && __GLIBC__ >= 2
#ifndef JB_SP
#define JB_SP 4
#endif
#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP]
#else
/* not an error but certainly cause for caution */
#error "Untested use of old glibc on i386"
#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp
#endif
#elif defined(__amd64__) || defined(__x86_64__)
#define MD_STACK_GROWS_DOWN
#define MD_USE_BUILTIN_SETJMP
#ifndef JB_RSP
#define JB_RSP 6
#endif
#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP]
#elif defined(__arm__)
#define MD_STACK_GROWS_DOWN
#if defined(__GLIBC__) && __GLIBC__ >= 2
#define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8]
#else
#error "ARM/Linux pre-glibc2 not supported yet"
#endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
#else
#error "Unknown CPU architecture"
#endif /* Cases with common MD_INIT_CONTEXT and different SP locations */
#endif /* Cases with different MD_INIT_CONTEXT */
#if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP)
/* i386/x86_64 */
#define MD_SETJMP(env) _st_md_cxt_save(env)
#define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val)
extern int _st_md_cxt_save(jmp_buf env);
extern void _st_md_cxt_restore(jmp_buf env, int val);
#else
/* arm/mips */
#define MD_SETJMP(env) setjmp(env)
#define MD_LONGJMP(env, val) longjmp(env, val)
#endif
/*****************************************
* Other defines
*/
#ifndef MD_STACK_PAD_SIZE
#define MD_STACK_PAD_SIZE 128
#endif
#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t)
#define socklen_t int
#endif
#ifndef MD_CAP_STACK
#define MD_CAP_STACK(var_addr)
#endif
#endif /* !__ST_MD_H__ */

@ -1,164 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
*
* Portions created by SGI are Copyright (C) 2000-2001 Silicon
* Graphics, Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
#ifndef __ST_THREAD_H__
#define __ST_THREAD_H__
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <time.h>
#include <errno.h>
#include <poll.h>
#define ST_VERSION "1.9"
#define ST_VERSION_MAJOR 1
#define ST_VERSION_MINOR 9
/* Undefine this to remove the context switch callback feature. */
#define ST_SWITCH_CB
#ifndef ETIME
#define ETIME ETIMEDOUT
#endif
#ifndef ST_UTIME_NO_TIMEOUT
#define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL)
#endif
#ifndef ST_UTIME_NO_WAIT
#define ST_UTIME_NO_WAIT 0
#endif
#define ST_EVENTSYS_DEFAULT 0
#define ST_EVENTSYS_SELECT 1
#define ST_EVENTSYS_POLL 2
#define ST_EVENTSYS_ALT 3
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned long long st_utime_t;
typedef struct _st_thread * st_thread_t;
typedef struct _st_cond * st_cond_t;
typedef struct _st_mutex * st_mutex_t;
typedef struct _st_netfd * st_netfd_t;
#ifdef ST_SWITCH_CB
typedef void (*st_switch_cb_t)(void);
#endif
extern int st_init(void);
extern int st_getfdlimit(void);
extern int st_set_eventsys(int eventsys);
extern int st_get_eventsys(void);
extern const char *st_get_eventsys_name(void);
#ifdef ST_SWITCH_CB
extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb);
extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb);
#endif
extern st_thread_t st_thread_self(void);
extern void st_thread_exit(void *retval);
extern int st_thread_join(st_thread_t trd, void **retvalp);
extern void st_thread_interrupt(st_thread_t trd);
extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size);
extern int st_randomize_stacks(int on);
extern int st_set_utime_function(st_utime_t (*func)(void));
extern st_utime_t st_utime(void);
extern st_utime_t st_utime_last_clock(void);
extern int st_timecache_set(int on);
extern time_t st_time(void);
extern int st_usleep(st_utime_t usecs);
extern int st_sleep(int secs);
extern st_cond_t st_cond_new(void);
extern int st_cond_destroy(st_cond_t cvar);
extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout);
extern int st_cond_wait(st_cond_t cvar);
extern int st_cond_signal(st_cond_t cvar);
extern int st_cond_broadcast(st_cond_t cvar);
extern st_mutex_t st_mutex_new(void);
extern int st_mutex_destroy(st_mutex_t lock);
extern int st_mutex_lock(st_mutex_t lock);
extern int st_mutex_unlock(st_mutex_t lock);
extern int st_mutex_trylock(st_mutex_t lock);
extern int st_key_create(int *keyp, void (*destructor)(void *));
extern int st_key_getlimit(void);
extern int st_thread_setspecific(int key, void *value);
extern void *st_thread_getspecific(int key);
extern st_netfd_t st_netfd_open(int osfd);
extern st_netfd_t st_netfd_open_socket(int osfd);
extern void st_netfd_free(st_netfd_t fd);
extern int st_netfd_close(st_netfd_t fd);
extern int st_netfd_fileno(st_netfd_t fd);
extern void st_netfd_setspecific(st_netfd_t fd, void *value, void (*destructor)(void *));
extern void *st_netfd_getspecific(st_netfd_t fd);
extern int st_netfd_serialize_accept(st_netfd_t fd);
extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout);
extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout);
extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout);
extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, st_utime_t timeout);
extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout);
extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout);
extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, st_utime_t timeout);
extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, st_utime_t timeout);
extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout);
extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout);
extern int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout);
extern int st_sendto(st_netfd_t fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout);
extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, st_utime_t timeout);
extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, st_utime_t timeout);
extern st_netfd_t st_open(const char *path, int oflags, mode_t mode);
#ifdef DEBUG
extern void _st_show_thread_stack(st_thread_t thread, const char *messg);
extern void _st_iterate_threads(void);
#endif
#ifdef __cplusplus
}
#endif
#endif /* !__ST_THREAD_H__ */

@ -1,680 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
*
* Portions created by SGI are Copyright (C) 2000-2001 Silicon
* Graphics, Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* This file is derived directly from Netscape Communications Corporation,
* and consists of extensive modifications made during the year(s) 1999-2000.
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "common.h"
/* Global data */
_st_vp_t _st_this_vp; /* This VP */
_st_thread_t *_st_this_thread; /* Current thread */
int _st_active_count = 0; /* Active thread count */
time_t _st_curr_time = 0; /* Current time as returned by time(2) */
st_utime_t _st_last_tset; /* Last time it was fetched */
int st_poll(struct pollfd *pds, int npds, st_utime_t timeout)
{
struct pollfd *pd;
struct pollfd *epd = pds + npds;
_st_pollq_t pq;
_st_thread_t *me = _ST_CURRENT_THREAD();
int n;
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
errno = EINTR;
return -1;
}
if ((*_st_eventsys->pollset_add)(pds, npds) < 0) {
return -1;
}
pq.pds = pds;
pq.npds = npds;
pq.thread = me;
pq.on_ioq = 1;
_ST_ADD_IOQ(pq);
if (timeout != ST_UTIME_NO_TIMEOUT) {
_ST_ADD_SLEEPQ(me, timeout);
}
me->state = _ST_ST_IO_WAIT;
_ST_SWITCH_CONTEXT(me);
n = 0;
if (pq.on_ioq) {
/* If we timed out, the pollq might still be on the ioq. Remove it */
_ST_DEL_IOQ(pq);
(*_st_eventsys->pollset_del)(pds, npds);
} else {
/* Count the number of ready descriptors */
for (pd = pds; pd < epd; pd++) {
if (pd->revents) {
n++;
}
}
}
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
errno = EINTR;
return -1;
}
return n;
}
void _st_vp_schedule(void)
{
_st_thread_t *trd;
if (_ST_RUNQ.next != &_ST_RUNQ) {
/* Pull thread off of the run queue */
trd = _ST_THREAD_PTR(_ST_RUNQ.next);
_ST_DEL_RUNQ(trd);
} else {
/* If there are no threads to run, switch to the idle thread */
trd = _st_this_vp.idle_thread;
}
ST_ASSERT(trd->state == _ST_ST_RUNNABLE);
/* Resume the thread */
trd->state = _ST_ST_RUNNING;
_ST_RESTORE_CONTEXT(trd);
}
/*
* Initialize this Virtual Processor
*/
int st_init(void)
{
_st_thread_t *trd;
if (_st_active_count) {
/* Already initialized */
return 0;
}
/* We can ignore return value here */
st_set_eventsys(ST_EVENTSYS_DEFAULT);
if (_st_io_init() < 0) {
return -1;
}
memset(&_st_this_vp, 0, sizeof(_st_vp_t));
ST_INIT_CLIST(&_ST_RUNQ);
ST_INIT_CLIST(&_ST_IOQ);
ST_INIT_CLIST(&_ST_ZOMBIEQ);
#ifdef DEBUG
ST_INIT_CLIST(&_ST_THREADQ);
#endif
if ((*_st_eventsys->init)() < 0) {
return -1;
}
_st_this_vp.pagesize = getpagesize();
_st_this_vp.last_clock = st_utime();
/*
* Create idle thread
*/
_st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, NULL, 0, 0);
if (!_st_this_vp.idle_thread) {
return -1;
}
_st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD;
_st_active_count--;
_ST_DEL_RUNQ(_st_this_vp.idle_thread);
/*
* Initialize primordial thread
*/
trd = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) +
(ST_KEYS_MAX * sizeof(void *)));
if (!trd) {
return -1;
}
trd->private_data = (void **) (trd + 1);
trd->state = _ST_ST_RUNNING;
trd->flags = _ST_FL_PRIMORDIAL;
_ST_SET_CURRENT_THREAD(trd);
_st_active_count++;
#ifdef DEBUG
_ST_ADD_THREADQ(trd);
#endif
return 0;
}
#ifdef ST_SWITCH_CB
st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb)
{
st_switch_cb_t ocb = _st_this_vp.switch_in_cb;
_st_this_vp.switch_in_cb = cb;
return ocb;
}
st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb)
{
st_switch_cb_t ocb = _st_this_vp.switch_out_cb;
_st_this_vp.switch_out_cb = cb;
return ocb;
}
#endif
/*
* Start function for the idle thread
*/
/* ARGSUSED */
void *_st_idle_thread_start(void *arg)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
while (_st_active_count > 0) {
/* Idle vp till I/O is ready or the smallest timeout expired */
_ST_VP_IDLE();
/* Check sleep queue for expired threads */
_st_vp_check_clock();
me->state = _ST_ST_RUNNABLE;
_ST_SWITCH_CONTEXT(me);
}
/* No more threads */
exit(0);
/* NOTREACHED */
return NULL;
}
void st_thread_exit(void *retval)
{
_st_thread_t *trd = _ST_CURRENT_THREAD();
trd->retval = retval;
_st_thread_cleanup(trd);
_st_active_count--;
if (trd->term) {
/* Put thread on the zombie queue */
trd->state = _ST_ST_ZOMBIE;
_ST_ADD_ZOMBIEQ(trd);
/* Notify on our termination condition variable */
st_cond_signal(trd->term);
/* Switch context and come back later */
_ST_SWITCH_CONTEXT(trd);
/* Continue the cleanup */
st_cond_destroy(trd->term);
trd->term = NULL;
}
#ifdef DEBUG
_ST_DEL_THREADQ(trd);
#endif
if (!(trd->flags & _ST_FL_PRIMORDIAL)) {
_st_stack_free(trd->stack);
}
/* Find another thread to run */
_ST_SWITCH_CONTEXT(trd);
/* Not going to land here */
}
int st_thread_join(_st_thread_t *trd, void **retvalp)
{
_st_cond_t *term = trd->term;
/* Can't join a non-joinable thread */
if (term == NULL) {
errno = EINVAL;
return -1;
}
if (_ST_CURRENT_THREAD() == trd) {
errno = EDEADLK;
return -1;
}
/* Multiple threads can't wait on the same joinable thread */
if (term->wait_q.next != &term->wait_q) {
errno = EINVAL;
return -1;
}
while (trd->state != _ST_ST_ZOMBIE) {
if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) {
return -1;
}
}
if (retvalp) {
*retvalp = trd->retval;
}
/*
* Remove target thread from the zombie queue and make it runnable.
* When it gets scheduled later, it will do the clean up.
*/
trd->state = _ST_ST_RUNNABLE;
_ST_DEL_ZOMBIEQ(trd);
_ST_ADD_RUNQ(trd);
return 0;
}
void _st_thread_main(void)
{
_st_thread_t *trd = _ST_CURRENT_THREAD();
/*
* Cap the stack by zeroing out the saved return address register
* value. This allows some debugging/profiling tools to know when
* to stop unwinding the stack. It's a no-op on most platforms.
*/
MD_CAP_STACK(&trd);
/* Run thread main */
trd->retval = (*trd->start)(trd->arg);
/* All done, time to go away */
st_thread_exit(trd->retval);
}
/*
* Insert "thread" into the timeout heap, in the position
* specified by thread->heap_index. See docs/timeout_heap.txt
* for details about the timeout heap.
*/
static _st_thread_t **heap_insert(_st_thread_t *trd)
{
int target = trd->heap_index;
int s = target;
_st_thread_t **p = &_ST_SLEEPQ;
int bits = 0;
int bit;
int index = 1;
while (s) {
s >>= 1;
bits++;
}
for (bit = bits - 2; bit >= 0; bit--) {
if (trd->due < (*p)->due) {
_st_thread_t *t = *p;
trd->left = t->left;
trd->right = t->right;
*p = trd;
trd->heap_index = index;
trd = t;
}
index <<= 1;
if (target & (1 << bit)) {
p = &((*p)->right);
index |= 1;
} else {
p = &((*p)->left);
}
}
trd->heap_index = index;
*p = trd;
trd->left = trd->right = NULL;
return p;
}
/*
* Delete "thread" from the timeout heap.
*/
static void heap_delete(_st_thread_t *trd)
{
_st_thread_t *t, **p;
int bits = 0;
int s, bit;
/* First find and unlink the last heap element */
p = &_ST_SLEEPQ;
s = _ST_SLEEPQ_SIZE;
while (s) {
s >>= 1;
bits++;
}
for (bit = bits - 2; bit >= 0; bit--) {
if (_ST_SLEEPQ_SIZE & (1 << bit)) {
p = &((*p)->right);
} else {
p = &((*p)->left);
}
}
t = *p;
*p = NULL;
--_ST_SLEEPQ_SIZE;
if (t != trd) {
/*
* Insert the unlinked last element in place of the element we are deleting
*/
t->heap_index = trd->heap_index;
p = heap_insert(t);
t = *p;
t->left = trd->left;
t->right = trd->right;
/*
* Reestablish the heap invariant.
*/
for (;;) {
_st_thread_t *y; /* The younger child */
int index_tmp;
if (t->left == NULL) {
break;
} else if (t->right == NULL) {
y = t->left;
} else if (t->left->due < t->right->due) {
y = t->left;
} else {
y = t->right;
}
if (t->due > y->due) {
_st_thread_t *tl = y->left;
_st_thread_t *tr = y->right;
*p = y;
if (y == t->left) {
y->left = t;
y->right = t->right;
p = &y->left;
} else {
y->left = t->left;
y->right = t;
p = &y->right;
}
t->left = tl;
t->right = tr;
index_tmp = t->heap_index;
t->heap_index = y->heap_index;
y->heap_index = index_tmp;
} else {
break;
}
}
}
trd->left = trd->right = NULL;
}
void _st_add_sleep_q(_st_thread_t *trd, st_utime_t timeout)
{
trd->due = _ST_LAST_CLOCK + timeout;
trd->flags |= _ST_FL_ON_SLEEPQ;
trd->heap_index = ++_ST_SLEEPQ_SIZE;
heap_insert(trd);
}
void _st_del_sleep_q(_st_thread_t *trd)
{
heap_delete(trd);
trd->flags &= ~_ST_FL_ON_SLEEPQ;
}
void _st_vp_check_clock(void)
{
_st_thread_t *trd;
st_utime_t elapsed, now;
now = st_utime();
elapsed = now - _ST_LAST_CLOCK;
_ST_LAST_CLOCK = now;
if (_st_curr_time && now - _st_last_tset > 999000) {
_st_curr_time = time(NULL);
_st_last_tset = now;
}
while (_ST_SLEEPQ != NULL) {
trd = _ST_SLEEPQ;
ST_ASSERT(trd->flags & _ST_FL_ON_SLEEPQ);
if (trd->due > now) {
break;
}
_ST_DEL_SLEEPQ(trd);
/* If thread is waiting on condition variable, set the time out flag */
if (trd->state == _ST_ST_COND_WAIT) {
trd->flags |= _ST_FL_TIMEDOUT;
}
/* Make thread runnable */
ST_ASSERT(!(trd->flags & _ST_FL_IDLE_THREAD));
trd->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(trd);
}
}
void st_thread_interrupt(_st_thread_t* trd)
{
/* If thread is already dead */
if (trd->state == _ST_ST_ZOMBIE) {
return;
}
trd->flags |= _ST_FL_INTERRUPT;
if (trd->state == _ST_ST_RUNNING || trd->state == _ST_ST_RUNNABLE) {
return;
}
if (trd->flags & _ST_FL_ON_SLEEPQ) {
_ST_DEL_SLEEPQ(trd);
}
/* Make thread runnable */
trd->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(trd);
}
_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size)
{
_st_thread_t *trd;
_st_stack_t *stack;
void **ptds;
char *sp;
/* Adjust stack size */
if (stk_size == 0) {
stk_size = ST_DEFAULT_STACK_SIZE;
}
stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE;
stack = _st_stack_new(stk_size);
if (!stack) {
return NULL;
}
/* Allocate thread object and per-thread data off the stack */
#if defined (MD_STACK_GROWS_DOWN)
sp = stack->stk_top;
/*
* The stack segment is split in the middle. The upper half is used
* as backing store for the register stack which grows upward.
* The lower half is used for the traditional memory stack which
* grows downward. Both stacks start in the middle and grow outward
* from each other.
*/
/**
The below comments is by winlin:
The Stack public structure:
+--------------------------------------------------------------+
| stack |
+--------------------------------------------------------------+
bottom top
The code bellow use the stack as:
+-----------------+-----------------+-------------+------------+
| stack of thread |pad+align(128B+) |thread(336B) | keys(128B) |
+-----------------+-----------------+-------------+------------+
bottom sp trd ptds top
(context[0].__jmpbuf.sp) (private_data)
*/
sp = sp - (ST_KEYS_MAX * sizeof(void *));
ptds = (void **) sp;
sp = sp - sizeof(_st_thread_t);
trd = (_st_thread_t *) sp;
/* Make stack 64-byte aligned */
if ((unsigned long)sp & 0x3f) {
sp = sp - ((unsigned long)sp & 0x3f);
}
stack->sp = sp - _ST_STACK_PAD_SIZE;
#else
#error "Only Supports Stack Grown Down"
#endif
memset(trd, 0, sizeof(_st_thread_t));
memset(ptds, 0, ST_KEYS_MAX * sizeof(void *));
/* Initialize thread */
trd->private_data = ptds;
trd->stack = stack;
trd->start = start;
trd->arg = arg;
// by winlin, expand macro MD_INIT_CONTEXT
#if defined(__mips__)
MD_SETJMP((trd)->context);
trd->context[0].__jmpbuf[0].__pc = (__ptr_t) _st_thread_main;
trd->context[0].__jmpbuf[0].__sp = stack->sp;
#else
if (MD_SETJMP((trd)->context)) {
_st_thread_main();
}
MD_GET_SP(trd) = (long) (stack->sp);
#endif
/* If thread is joinable, allocate a termination condition variable */
if (joinable) {
trd->term = st_cond_new();
if (trd->term == NULL) {
_st_stack_free(trd->stack);
return NULL;
}
}
/* Make thread runnable */
trd->state = _ST_ST_RUNNABLE;
_st_active_count++;
_ST_ADD_RUNQ(trd);
#ifdef DEBUG
_ST_ADD_THREADQ(trd);
#endif
return trd;
}
_st_thread_t *st_thread_self(void)
{
return _ST_CURRENT_THREAD();
}
#ifdef DEBUG
/* ARGSUSED */
void _st_show_thread_stack(_st_thread_t *trd, const char *messg)
{
}
/* To be set from debugger */
int _st_iterate_threads_flag = 0;
void _st_iterate_threads(void)
{
static _st_thread_t *trd = NULL;
static jmp_buf orig_jb, save_jb;
_st_clist_t *q;
if (!_st_iterate_threads_flag) {
if (trd) {
memcpy(trd->context, save_jb, sizeof(jmp_buf));
MD_LONGJMP(orig_jb, 1);
}
return;
}
if (trd) {
memcpy(trd->context, save_jb, sizeof(jmp_buf));
_st_show_thread_stack(trd, NULL);
} else {
if (MD_SETJMP(orig_jb)) {
_st_iterate_threads_flag = 0;
trd = NULL;
_st_show_thread_stack(trd, "Iteration completed");
return;
}
trd = _ST_CURRENT_THREAD();
_st_show_thread_stack(trd, "Iteration started");
}
q = trd->tlink.next;
if (q == &_ST_THREADQ) {
q = q->next;
}
ST_ASSERT(q != &_ST_THREADQ);
trd = _ST_THREAD_THREADQ_PTR(q);
if (trd == _ST_CURRENT_THREAD()) {
MD_LONGJMP(orig_jb, 1);
}
memcpy(save_jb, trd->context, sizeof(jmp_buf));
MD_LONGJMP(trd->context, 1);
}
#endif /* DEBUG */

@ -1,497 +0,0 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "public.h"
#define srs_trace(msg, ...) printf(msg, ##__VA_ARGS__);printf("\n")
int io_port = 1990;
int sleep_ms = 100;
void stack_print(long int previous_sp, int level)
{
if (level <= 0) {
return;
}
register long int rsp asm("sp");
char buf[level * 1024];
stack_print(rsp, level - 1);
srs_trace("%d. psp=%#lx, sp=%#lx, size=%dB(%dB+%dKB)",
level, previous_sp, rsp, (int)(previous_sp - rsp),
(int)(previous_sp - rsp - sizeof(buf)), (int)(sizeof(buf) / 1024));
}
int huge_stack_test()
{
srs_trace("===================================================");
srs_trace("huge_stack test: start");
register long int rsp asm("sp");
stack_print(rsp, 10);
srs_trace("huge_stack test: end");
return 0;
}
int sleep_test()
{
srs_trace("===================================================");
srs_trace("sleep test: start");
srs_trace("1. sleep...");
st_utime_t start = st_utime();
st_usleep(sleep_ms * 1000);
st_utime_t end = st_utime();
srs_trace("2. sleep ok, sleep=%dus, deviation=%dus",
(int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000));
srs_trace("sleep test: end");
return 0;
}
void* sleep2_func0(void* arg)
{
int sleep_ms = 100;
st_utime_t start = st_utime();
st_usleep(sleep_ms * 1000);
st_utime_t end = st_utime();
srs_trace("sleep ok, sleep=%dus, deviation=%dus",
(int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000));
return NULL;
}
void* sleep2_func1(void* arg)
{
int sleep_ms = 250;
st_utime_t start = st_utime();
st_usleep(sleep_ms * 1000);
st_utime_t end = st_utime();
srs_trace("sleep ok, sleep=%dus, deviation=%dus",
(int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000));
return NULL;
}
int sleep2_test()
{
srs_trace("===================================================");
srs_trace("sleep2 test: start");
st_thread_t trd0 = st_thread_create(sleep2_func0, NULL, 1, 0);
st_thread_t trd1 = st_thread_create(sleep2_func1, NULL, 1, 0);
st_thread_join(trd0, NULL);
st_thread_join(trd1, NULL);
srs_trace("sleep test: end");
return 0;
}
st_mutex_t sleep_work_cond = NULL;
void* sleep_deviation_func(void* arg)
{
st_mutex_lock(sleep_work_cond);
srs_trace("2. work thread start.");
int64_t i;
for (i = 0; i < 3000000000ULL; i++) {
}
st_mutex_unlock(sleep_work_cond);
srs_trace("3. work thread end.");
return NULL;
}
int sleep_deviation_test()
{
srs_trace("===================================================");
srs_trace("sleep deviation test: start");
sleep_work_cond = st_mutex_new();
st_thread_create(sleep_deviation_func, NULL, 0, 0);
st_mutex_lock(sleep_work_cond);
srs_trace("1. sleep...");
st_utime_t start = st_utime();
// other thread to do some complex work.
st_mutex_unlock(sleep_work_cond);
st_usleep(1000 * 1000);
st_utime_t end = st_utime();
srs_trace("4. sleep ok, sleep=%dus, deviation=%dus",
(int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000));
st_mutex_lock(sleep_work_cond);
srs_trace("sleep deviation test: end");
st_mutex_destroy(sleep_work_cond);
return 0;
}
void* thread_func(void* arg)
{
srs_trace("1. thread run");
st_usleep(sleep_ms * 1000);
srs_trace("2. thread completed");
return NULL;
}
int thread_test()
{
srs_trace("===================================================");
srs_trace("thread test: start");
st_thread_t trd = st_thread_create(thread_func, NULL, 1, 0);
if (trd == NULL) {
srs_trace("st_thread_create failed");
return -1;
}
st_thread_join(trd, NULL);
srs_trace("3. thread joined");
srs_trace("thread test: end");
return 0;
}
st_mutex_t sync_start = NULL;
st_cond_t sync_cond = NULL;
st_mutex_t sync_mutex = NULL;
st_cond_t sync_end = NULL;
void* sync_master(void* arg)
{
// wait for main to sync_start this thread.
st_mutex_lock(sync_start);
st_mutex_unlock(sync_start);
st_usleep(sleep_ms * 1000);
st_cond_signal(sync_cond);
st_mutex_lock(sync_mutex);
srs_trace("2. st mutex is ok");
st_mutex_unlock(sync_mutex);
st_usleep(sleep_ms * 1000);
srs_trace("3. st thread is ok");
st_cond_signal(sync_cond);
return NULL;
}
void* sync_slave(void* arg)
{
// lock mutex to control thread.
st_mutex_lock(sync_mutex);
// wait for main to sync_start this thread.
st_mutex_lock(sync_start);
st_mutex_unlock(sync_start);
// wait thread to ready.
st_cond_wait(sync_cond);
srs_trace("1. st cond is ok");
// release mutex to control thread
st_usleep(sleep_ms * 1000);
st_mutex_unlock(sync_mutex);
// wait thread to exit.
st_cond_wait(sync_cond);
srs_trace("4. st is ok");
st_cond_signal(sync_end);
return NULL;
}
int sync_test()
{
srs_trace("===================================================");
srs_trace("sync test: start");
if ((sync_start = st_mutex_new()) == NULL) {
srs_trace("st_mutex_new sync_start failed");
return -1;
}
st_mutex_lock(sync_start);
if ((sync_cond = st_cond_new()) == NULL) {
srs_trace("st_cond_new cond failed");
return -1;
}
if ((sync_end = st_cond_new()) == NULL) {
srs_trace("st_cond_new end failed");
return -1;
}
if ((sync_mutex = st_mutex_new()) == NULL) {
srs_trace("st_mutex_new mutex failed");
return -1;
}
if (!st_thread_create(sync_master, NULL, 0, 0)) {
srs_trace("st_thread_create failed");
return -1;
}
if (!st_thread_create(sync_slave, NULL, 0, 0)) {
srs_trace("st_thread_create failed");
return -1;
}
// run all threads.
st_mutex_unlock(sync_start);
st_cond_wait(sync_end);
srs_trace("sync test: end");
return 0;
}
void* io_client(void* arg)
{
int fd;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
srs_trace("create linux socket error.");
return NULL;
}
srs_trace("6. client create linux socket success. fd=%d", fd);
st_netfd_t stfd;
if ((stfd = st_netfd_open_socket(fd)) == NULL){
srs_trace("st_netfd_open_socket open socket failed.");
return NULL;
}
srs_trace("7. client st open socket success. fd=%d", fd);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(io_port);
addr.sin_addr.s_addr = INADDR_ANY;
if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1) {
srs_trace("bind socket error.");
return NULL;
}
char buf[1024];
if (st_read_fully(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) {
srs_trace("st_read_fully failed");
return NULL;
}
if (st_write(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) {
srs_trace("st_write failed");
return NULL;
}
st_netfd_close(stfd);
return NULL;
}
int io_test()
{
srs_trace("===================================================");
srs_trace("io test: start, port=%d", io_port);
int fd;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
srs_trace("create linux socket error.");
return -1;
}
srs_trace("1. server create linux socket success. fd=%d", fd);
int reuse_socket = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
srs_trace("setsockopt reuse-addr error.");
return -1;
}
srs_trace("2. server setsockopt reuse-addr success. fd=%d", fd);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(io_port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) {
srs_trace("bind socket error.");
return -1;
}
srs_trace("3. server bind socket success. fd=%d", fd);
if (listen(fd, 10) == -1) {
srs_trace("listen socket error.");
return -1;
}
srs_trace("4. server listen socket success. fd=%d", fd);
st_netfd_t stfd;
if ((stfd = st_netfd_open_socket(fd)) == NULL){
srs_trace("st_netfd_open_socket open socket failed.");
return -1;
}
srs_trace("5. server st open socket success. fd=%d", fd);
if (!st_thread_create(io_client, NULL, 0, 0)) {
srs_trace("st_thread_create failed");
return -1;
}
st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
srs_trace("8. server get a client. fd=%d", st_netfd_fileno(client_stfd));
char buf[1024];
if (st_write(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) {
srs_trace("st_write failed");
return -1;
}
if (st_read_fully(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) {
srs_trace("st_read_fully failed");
return -1;
}
srs_trace("9. server io completed.");
st_netfd_close(stfd);
st_netfd_close(client_stfd);
srs_trace("io test: end");
return 0;
}
int pipe_test()
{
srs_trace("===================================================");
srs_trace("pipe test: start");
int fds[2];
if (pipe(fds) < 0) {
srs_trace("pipe failed");
return -1;
}
srs_trace("1. pipe ok, %d=>%d", fds[1], fds[0]);
st_netfd_t fdw;
if ((fdw = st_netfd_open_socket(fds[1])) == NULL) {
srs_trace("st_netfd_open_socket open socket failed.");
return -1;
}
srs_trace("2. open write fd ok");
st_netfd_t fdr;
if ((fdr = st_netfd_open_socket(fds[0])) == NULL) {
srs_trace("st_netfd_open_socket open socket failed.");
return -1;
}
srs_trace("3. open read fd ok");
char buf[1024];
if (st_write(fdw, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) {
srs_trace("st_write socket failed.");
return -1;
}
srs_trace("4. write to pipe ok");
if (st_read(fdr, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) {
srs_trace("st_read socket failed.");
return -1;
}
srs_trace("5. read from pipe ok");
st_netfd_close(fdw);
st_netfd_close(fdr);
srs_trace("pipe test: end");
return 0;
}
int main(int argc, char** argv)
{
srs_trace("ETIME=%d", ETIME);
if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) {
srs_trace("st_set_eventsys failed");
return -1;
}
if (st_init() < 0) {
srs_trace("st_init failed");
return -1;
}
if (sleep2_test() < 0) {
srs_trace("sleep2_test failed");
return -1;
}
if (sleep_test() < 0) {
srs_trace("sleep_test failed");
return -1;
}
if (sleep_deviation_test() < 0) {
srs_trace("sleep_deviation_test failed");
return -1;
}
if (huge_stack_test() < 0) {
srs_trace("huge_stack_test failed");
return -1;
}
if (thread_test() < 0) {
srs_trace("thread_test failed");
return -1;
}
if (sync_test() < 0) {
srs_trace("sync_test failed");
return -1;
}
if (io_test() < 0) {
srs_trace("io_test failed");
return -1;
}
if (pipe_test() < 0) {
srs_trace("pipe_test failed");
return -1;
}
// cleanup.
srs_trace("wait for all thread completed");
st_thread_exit(NULL);
// the following never enter,
// the above code will exit when all thread exit,
// current is a primordial st-thread, when all thread exit,
// the st idle thread will exit(0), see _st_idle_thread_start()
srs_trace("all thread completed");
return 0;
}

@ -1,3 +0,0 @@
#ifndef _st_icpp_init_stub
#define _st_icpp_init_stub
#endif

@ -1,18 +0,0 @@
file
main readonly separator,
..\srs.c,
st readonly separator,
..\common.h,
..\event.c,
..\io.c,
..\key.c,
..\md.h,
..\md.S,
..\public.h,
..\sched.c,
..\stk.c,
..\sync.c;
mainconfig
"" = "MAIN";

@ -1,169 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
*
* Portions created by SGI are Copyright (C) 2000-2001 Silicon
* Graphics, Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* This file is derived directly from Netscape Communications Corporation,
* and consists of extensive modifications made during the year(s) 1999-2000.
*/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "common.h"
/* How much space to leave between the stacks, at each end */
#define REDZONE _ST_PAGE_SIZE
_st_clist_t _st_free_stacks = ST_INIT_STATIC_CLIST(&_st_free_stacks);
int _st_num_free_stacks = 0;
int _st_randomize_stacks = 0;
static char *_st_new_stk_segment(int size);
/**
The below comments is by winlin:
The stack memory struct:
| REDZONE | stack | extra | REDZONE |
+---------+------------------------+---------+---------+
| 4k | | 4k/0 | 4k |
+---------+------------------------+---------+---------+
vaddr bottom top
When _st_randomize_stacks is on, by st_randomize_stacks(),
the bottom and top will random movided in the extra:
long offset = (random() % extra) & ~0xf;
ts->stk_bottom += offset;
ts->stk_top += offset;
Both REDZONE are protected by mprotect when DEBUG is on.
*/
_st_stack_t *_st_stack_new(int stack_size)
{
_st_clist_t *qp;
_st_stack_t *ts;
int extra;
// TODO: WINLIN: remove the stack reuse.
for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) {
ts = _ST_THREAD_STACK_PTR(qp);
if (ts->stk_size >= stack_size) {
/* Found a stack that is big enough */
ST_REMOVE_LINK(&ts->links);
_st_num_free_stacks--;
ts->links.next = NULL;
ts->links.prev = NULL;
return ts;
}
}
/* Make a new thread stack object. */
if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) {
return NULL;
}
extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0;
ts->vaddr_size = stack_size + 2*REDZONE + extra;
ts->vaddr = _st_new_stk_segment(ts->vaddr_size);
if (!ts->vaddr) {
free(ts);
return NULL;
}
ts->stk_size = stack_size;
ts->stk_bottom = ts->vaddr + REDZONE;
ts->stk_top = ts->stk_bottom + stack_size;
#ifdef DEBUG
mprotect(ts->vaddr, REDZONE, PROT_NONE);
mprotect(ts->stk_top + extra, REDZONE, PROT_NONE);
#endif
if (extra) {
long offset = (random() % extra) & ~0xf;
ts->stk_bottom += offset;
ts->stk_top += offset;
}
return ts;
}
/*
* Free the stack for the current thread
*/
void _st_stack_free(_st_stack_t *ts)
{
if (!ts) {
return;
}
/* Put the stack on the free list */
ST_APPEND_LINK(&ts->links, _st_free_stacks.prev);
_st_num_free_stacks++;
}
static char *_st_new_stk_segment(int size)
{
#ifdef MALLOC_STACK
void *vaddr = malloc(size);
#else
#error "Only Supports Malloc Stack"
#endif
return (char *)vaddr;
}
/* Not used */
#if 0
void _st_delete_stk_segment(char *vaddr, int size)
{
#ifdef MALLOC_STACK
free(vaddr);
#else
#error Unknown Stack Malloc
#endif
}
#endif
int st_randomize_stacks(int on)
{
int wason = _st_randomize_stacks;
_st_randomize_stacks = on;
if (on) {
srandom((unsigned int) st_utime());
}
return wason;
}

@ -1,352 +0,0 @@
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Portable Runtime library.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Silicon Graphics, Inc.
*
* Portions created by SGI are Copyright (C) 2000-2001 Silicon
* Graphics, Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* This file is derived directly from Netscape Communications Corporation,
* and consists of extensive modifications made during the year(s) 1999-2000.
*/
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include "common.h"
extern time_t _st_curr_time;
extern st_utime_t _st_last_tset;
extern int _st_active_count;
static st_utime_t (*_st_utime)(void) = NULL;
/*****************************************
* Time functions
*/
st_utime_t st_utime(void)
{
if (_st_utime == NULL) {
#ifdef MD_GET_UTIME
MD_GET_UTIME();
#else
#error Unknown OS
#endif
}
return (*_st_utime)();
}
int st_set_utime_function(st_utime_t (*func)(void))
{
if (_st_active_count) {
errno = EINVAL;
return -1;
}
_st_utime = func;
return 0;
}
st_utime_t st_utime_last_clock(void)
{
return _ST_LAST_CLOCK;
}
int st_timecache_set(int on)
{
int wason = (_st_curr_time) ? 1 : 0;
if (on) {
_st_curr_time = time(NULL);
_st_last_tset = st_utime();
} else {
_st_curr_time = 0;
}
return wason;
}
time_t st_time(void)
{
if (_st_curr_time) {
return _st_curr_time;
}
return time(NULL);
}
int st_usleep(st_utime_t usecs)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
errno = EINTR;
return -1;
}
if (usecs != ST_UTIME_NO_TIMEOUT) {
me->state = _ST_ST_SLEEPING;
_ST_ADD_SLEEPQ(me, usecs);
} else {
me->state = _ST_ST_SUSPENDED;
}
_ST_SWITCH_CONTEXT(me);
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
errno = EINTR;
return -1;
}
return 0;
}
int st_sleep(int secs)
{
return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : ST_UTIME_NO_TIMEOUT);
}
/*****************************************
* Condition variable functions
*/
_st_cond_t *st_cond_new(void)
{
_st_cond_t *cvar;
cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t));
if (cvar) {
ST_INIT_CLIST(&cvar->wait_q);
}
return cvar;
}
int st_cond_destroy(_st_cond_t *cvar)
{
if (cvar->wait_q.next != &cvar->wait_q) {
errno = EBUSY;
return -1;
}
free(cvar);
return 0;
}
int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
int rv;
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
errno = EINTR;
return -1;
}
/* Put caller thread on the condition variable's wait queue */
me->state = _ST_ST_COND_WAIT;
ST_APPEND_LINK(&me->wait_links, &cvar->wait_q);
if (timeout != ST_UTIME_NO_TIMEOUT) {
_ST_ADD_SLEEPQ(me, timeout);
}
_ST_SWITCH_CONTEXT(me);
ST_REMOVE_LINK(&me->wait_links);
rv = 0;
if (me->flags & _ST_FL_TIMEDOUT) {
me->flags &= ~_ST_FL_TIMEDOUT;
errno = ETIME;
rv = -1;
}
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
errno = EINTR;
rv = -1;
}
return rv;
}
int st_cond_wait(_st_cond_t *cvar)
{
return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT);
}
static int _st_cond_signal(_st_cond_t *cvar, int broadcast)
{
_st_thread_t *thread;
_st_clist_t *q;
for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) {
thread = _ST_THREAD_WAITQ_PTR(q);
if (thread->state == _ST_ST_COND_WAIT) {
if (thread->flags & _ST_FL_ON_SLEEPQ) {
_ST_DEL_SLEEPQ(thread);
}
/* Make thread runnable */
thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(thread);
if (!broadcast) {
break;
}
}
}
return 0;
}
int st_cond_signal(_st_cond_t *cvar)
{
return _st_cond_signal(cvar, 0);
}
int st_cond_broadcast(_st_cond_t *cvar)
{
return _st_cond_signal(cvar, 1);
}
/*****************************************
* Mutex functions
*/
_st_mutex_t *st_mutex_new(void)
{
_st_mutex_t *lock;
lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t));
if (lock) {
ST_INIT_CLIST(&lock->wait_q);
lock->owner = NULL;
}
return lock;
}
int st_mutex_destroy(_st_mutex_t *lock)
{
if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) {
errno = EBUSY;
return -1;
}
free(lock);
return 0;
}
int st_mutex_lock(_st_mutex_t *lock)
{
_st_thread_t *me = _ST_CURRENT_THREAD();
if (me->flags & _ST_FL_INTERRUPT) {
me->flags &= ~_ST_FL_INTERRUPT;
errno = EINTR;
return -1;
}
if (lock->owner == NULL) {
/* Got the mutex */
lock->owner = me;
return 0;
}
if (lock->owner == me) {
errno = EDEADLK;
return -1;
}
/* Put caller thread on the mutex's wait queue */
me->state = _ST_ST_LOCK_WAIT;
ST_APPEND_LINK(&me->wait_links, &lock->wait_q);
_ST_SWITCH_CONTEXT(me);
ST_REMOVE_LINK(&me->wait_links);
if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) {
me->flags &= ~_ST_FL_INTERRUPT;
errno = EINTR;
return -1;
}
return 0;
}
int st_mutex_unlock(_st_mutex_t *lock)
{
_st_thread_t *thread;
_st_clist_t *q;
if (lock->owner != _ST_CURRENT_THREAD()) {
errno = EPERM;
return -1;
}
for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) {
thread = _ST_THREAD_WAITQ_PTR(q);
if (thread->state == _ST_ST_LOCK_WAIT) {
lock->owner = thread;
/* Make thread runnable */
thread->state = _ST_ST_RUNNABLE;
_ST_ADD_RUNQ(thread);
return 0;
}
}
/* No threads waiting on this mutex */
lock->owner = NULL;
return 0;
}
int st_mutex_trylock(_st_mutex_t *lock)
{
if (lock->owner != NULL) {
errno = EBUSY;
return -1;
}
/* Got the mutex */
lock->owner = _ST_CURRENT_THREAD();
return 0;
}

@ -4747,7 +4747,8 @@ int SrsConfig::get_rtc_server_sendmmsg()
return DEFAULT;
}
return ::atoi(conf->arg0().c_str());
int v = ::atoi(conf->arg0().c_str());
return srs_max(1, v);
#endif
}

@ -528,17 +528,14 @@ public:
virtual int get_rtc_server_sendmmsg();
virtual bool get_rtc_server_encrypt();
virtual int get_rtc_server_reuseport();
private:
virtual int get_rtc_server_reuseport2();
public:
virtual bool get_rtc_server_merge_nalus();
virtual bool get_rtc_server_gso();
private:
virtual bool get_rtc_server_gso2();
public:
virtual int get_rtc_server_padding();
virtual bool get_rtc_server_perf_stat();
virtual int get_rtc_server_queue_length();
private:
virtual int get_rtc_server_reuseport2();
virtual bool get_rtc_server_gso2();
public:
SrsConfDirective* get_rtc(std::string vhost);

@ -103,7 +103,7 @@ srs_error_t SrsConnection::set_tcp_nodelay(bool v)
int iv = (v? 1:0);
if ((r0 = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &iv, nb_v)) != 0) {
return srs_error_new(ERROR_SOCKET_NO_NODELAY, "setsockopt fd=%d, r0=%v", fd, r0);
return srs_error_new(ERROR_SOCKET_NO_NODELAY, "setsockopt fd=%d, r0=%d", fd, r0);
}
if ((r0 = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &iv, &nb_v)) != 0) {
return srs_error_new(ERROR_SOCKET_NO_NODELAY, "getsockopt fd=%d, r0=%d", fd, r0);
@ -155,7 +155,7 @@ srs_error_t SrsConnection::set_socket_buffer(srs_utime_t buffer_v)
// set the socket send buffer when required larger buffer
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iv, nb_v) < 0) {
return srs_error_new(ERROR_SOCKET_SNDBUF, "setsockopt fd=%d, r0=%v", fd, r0);
return srs_error_new(ERROR_SOCKET_SNDBUF, "setsockopt fd=%d, r0=%d", fd, r0);
}
if ((r0 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iv, &nb_v)) != 0) {
return srs_error_new(ERROR_SOCKET_SNDBUF, "getsockopt fd=%d, r0=%d", fd, r0);

@ -34,6 +34,8 @@ using namespace std;
#include <unistd.h>
#include <netinet/udp.h>
// Define macro for UDP GSO.
// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/udpgso.c
#ifndef UDP_SEGMENT
#define UDP_SEGMENT 103
#endif

@ -296,7 +296,7 @@ srs_error_t SrsFileReader::lseek(off_t offset, int whence, off_t* seeked)
{
off_t sk = _srs_lseek_fn(fd, offset, whence);
if (sk < 0) {
return srs_error_new(ERROR_SYSTEM_FILE_SEEK, "seek %v failed", (int)sk);
return srs_error_new(ERROR_SYSTEM_FILE_SEEK, "seek %d failed", (int)sk);
}
if (seeked) {

@ -510,7 +510,7 @@ srs_error_t SrsMp4Box::encode_header(SrsBuffer* buf)
int lrsz = nb_header() - SrsMp4Box::nb_header();
if (!buf->require(lrsz)) {
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "box requires %v only %d bytes", lrsz, buf->left());
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "box requires %d only %d bytes", lrsz, buf->left());
}
return err;
@ -3602,19 +3602,19 @@ srs_error_t SrsMp4ES_Descriptor::decode_payload(SrsBuffer* buf)
if (streamDependenceFlag) {
if (!buf->require(2)) {
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "ES requires 2 only %v bytes", buf->left());
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "ES requires 2 only %d bytes", buf->left());
}
dependsOn_ES_ID = buf->read_2bytes();
}
if (URL_Flag) {
if (!buf->require(1)) {
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "URLlength requires 1 only %v bytes", buf->left());
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "URLlength requires 1 only %d bytes", buf->left());
}
uint8_t URLlength = buf->read_1bytes();
if (!buf->require(URLlength)) {
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "URL requires %d only %v bytes", URLlength, buf->left());
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "URL requires %d only %d bytes", URLlength, buf->left());
}
URLstring.resize(URLlength);
buf->read_bytes(&URLstring[0], URLlength);
@ -3622,7 +3622,7 @@ srs_error_t SrsMp4ES_Descriptor::decode_payload(SrsBuffer* buf)
if (OCRstreamFlag) {
if (!buf->require(2)) {
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "OCR requires 2 only %v bytes", buf->left());
return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "OCR requires 2 only %d bytes", buf->left());
}
OCR_ES_Id = buf->read_2bytes();
}

@ -94,7 +94,7 @@ srs_error_t srs_fd_closeexec(int fd)
int flags = fcntl(fd, F_GETFD);
flags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
return srs_error_new(ERROR_SOCKET_SETCLOSEEXEC, "FD_CLOEXEC fd=%v", fd);
return srs_error_new(ERROR_SOCKET_SETCLOSEEXEC, "FD_CLOEXEC fd=%d", fd);
}
return srs_success;
@ -104,7 +104,7 @@ srs_error_t srs_fd_reuseaddr(int fd)
{
int v = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(int)) == -1) {
return srs_error_new(ERROR_SOCKET_SETREUSEADDR, "SO_REUSEADDR fd=%v", fd);
return srs_error_new(ERROR_SOCKET_SETREUSEADDR, "SO_REUSEADDR fd=%d", fd);
}
return srs_success;
@ -119,7 +119,7 @@ srs_error_t srs_fd_reuseport(int fd)
srs_warn("SO_REUSEPORT disabled for crossbuild");
return srs_success;
#else
return srs_error_new(ERROR_SOCKET_SETREUSEADDR, "SO_REUSEPORT fd=%v", fd);
return srs_error_new(ERROR_SOCKET_SETREUSEADDR, "SO_REUSEPORT fd=%d", fd);
#endif
}
#else
@ -402,6 +402,11 @@ int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr * to,
return st_sendto((st_netfd_t)stfd, buf, len, to, tolen, (st_utime_t)timeout);
}
int srs_recvmsg(srs_netfd_t stfd, struct msghdr *msg, int flags, srs_utime_t timeout)
{
return st_recvmsg((st_netfd_t)stfd, msg, flags, (st_utime_t)timeout);
}
int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, srs_utime_t timeout)
{
return st_sendmsg((st_netfd_t)stfd, msg, flags, (st_utime_t)timeout);

@ -89,6 +89,7 @@ extern srs_netfd_t srs_netfd_open(int osfd);
extern int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, int *fromlen, srs_utime_t timeout);
extern int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr *to, int tolen, srs_utime_t timeout);
extern int srs_recvmsg(srs_netfd_t stfd, struct msghdr *msg, int flags, srs_utime_t timeout);
extern int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, srs_utime_t timeout);
#if !defined(SRS_AUTO_HAS_SENDMMSG)

Loading…
Cancel
Save