Browse Source

unchecked malloc return cases in libjack fixed; add info on -L option ; different backend lists in usage text for OS X and linux; indent ringbuffer.c appropriately

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@3771 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.117.0
paul 15 years ago
parent
commit
3bcd2ed32e
8 changed files with 279 additions and 212 deletions
  1. +14
    -3
      config/os/gnu-linux/sanitycheck.c
  2. +13
    -4
      config/os/gnu-linux/systemtest.c
  3. +9
    -4
      jackd/jackd.c
  4. +37
    -14
      libjack/client.c
  5. +5
    -3
      libjack/intclient.c
  6. +13
    -3
      libjack/port.c
  7. +185
    -180
      libjack/ringbuffer.c
  8. +3
    -1
      libjack/thread.c

+ 14
- 3
config/os/gnu-linux/sanitycheck.c View File

@@ -1,10 +1,21 @@
/**
* GPL etc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* @author Florian Faber
*
* @version 0.1 (2009-01-17) [FF]
* - initial version
**/

#include <stdio.h>


+ 13
- 4
config/os/gnu-linux/systemtest.c View File

@@ -1,14 +1,23 @@
/**
* GPL, yabbadabba
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Set of functions to gather system information for the jack setup wizard.
*
* TODO: Test for rt prio availability
*
* @author Florian Faber, faber@faberman.de
*
* @version 0.1 (2009-01-15) [FF]
* - initial version
*
**/



+ 9
- 4
jackd/jackd.c View File

@@ -352,7 +352,7 @@ jack_drivers_load ()
static void copyright (FILE* file)
{
fprintf (file, "jackd " VERSION "\n"
"Copyright 2001-2005 Paul Davis and others.\n"
"Copyright 2001-2009 Paul Davis, Stephane Letz, Jack O'Quinn, Torben Hohn and others.\n"
"jackd comes with ABSOLUTELY NO WARRANTY\n"
"This is free software, and you are welcome to redistribute it\n"
"under certain conditions; see the file COPYING for details\n\n");
@@ -362,7 +362,9 @@ static void usage (FILE *file)
{
copyright (file);
fprintf (file, "\n"
"usage: jackd [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n"
"usage: jackd [ --no-realtime OR -L ]\n"
" [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n"
" (the two previous arguments are mutually exclusive. The default is --realtime)\n"
" [ --name OR -n server-name ]\n"
" [ --no-mlock OR -m ]\n"
" [ --unlock OR -u ]\n"
@@ -377,8 +379,11 @@ static void usage (FILE *file)
" [ --version OR -V ]\n"
" [ --nozombies OR -Z ]\n"
" -d backend [ ... backend args ... ]\n"
" The backend can be `alsa', `coreaudio', `dummy',\n"
" `freebob', `oss', `sun', or `portaudio'.\n\n"
#ifdef __APPLE__
" Available backends may include: coreaudio, dummy, netjack, portaudio.\n\n"
#else
" Available backends may include: alsa, dummy, freebob, firewire, netjack, oss, sun, or portaudio.\n\n"
#endif
" jackd -d backend --help\n"
" to display options for each backend\n\n");
}


+ 37
- 14
libjack/client.c View File

@@ -133,7 +133,9 @@ jack_get_tmpdir ()

/* don't let strtok(3) mess with the real environment variable */

pathcopy = strdup (pathenv);
if ((pathcopy = strdup (pathenv)) == NULL) {
return -1;
}
p = strtok (pathcopy, ":");

while (p) {
@@ -175,12 +177,17 @@ jack_get_tmpdir ()
return -1;
}

jack_tmpdir = (char *) malloc (len);
if ((jack_tmpdir = (char *) malloc (len)) == NULL) {
free (pathcopy);
return -1;
}

memcpy (jack_tmpdir, buf, len-1);
jack_tmpdir[len-1] = '\0';
fclose (in);
free (pathcopy);

return 0;
}

@@ -284,8 +291,15 @@ jack_client_alloc ()
{
jack_client_t *client;

client = (jack_client_t *) malloc (sizeof (jack_client_t));
client->pollfd = (struct pollfd *) malloc (sizeof (struct pollfd) * 1);
if ((client = (jack_client_t *) malloc (sizeof (jack_client_t))) == NULL) {
return NULL;
}

if ((client->pollfd = (struct pollfd *) malloc (sizeof (struct pollfd) * 1)) == NULL) {
free (client);
return NULL;
}

client->pollmax = 1;
client->request_fd = -1;
client->event_fd = -1;
@@ -318,8 +332,14 @@ jack_client_alloc ()
{
jack_client_t *client;

client = (jack_client_t *) malloc (sizeof (jack_client_t));
client->pollfd = (struct pollfd *) malloc (sizeof (struct pollfd) * 2);
if ((client = (jack_client_t *) malloc (sizeof (jack_client_t))) == NULL) {
return NULL;
}
if ((client->pollfd = (struct pollfd *) malloc (sizeof (struct pollfd) * 2)) == NULL) {
free (client);
return NULL;
}

client->pollmax = 2;
client->request_fd = -1;
client->event_fd = -1;
@@ -681,12 +701,16 @@ _start_server (const char *server_name)
#endif /* USE_CAPABILITIES */
} else {
result = strcspn(arguments, " ");
command = (char *) malloc(result+1);
if ((command = (char *) malloc(result+1)) == NULL) {
goto failure;
}
strncpy(command, arguments, result);
command[result] = '\0';
}

argv = (char **) malloc (255);
if ((argv = (char **) malloc (255)) == NULL) {
goto failure;
}
while(1) {
/* insert -T and -nserver_name in front of arguments */
@@ -727,6 +751,7 @@ _start_server (const char *server_name)

execv (command, argv);

failure:
/* If execv() succeeds, it does not return. There's no point
* in calling jack_error() here in the child process. */
fprintf (stderr, "exec of JACK server (command = \"%s\") failed: %s\n", command, strerror (errno));
@@ -1050,8 +1075,9 @@ jack_client_open_aux (const char *client_name,
jack_destroy_shm (&client->control_shm);

client->n_port_types = client->engine->n_port_types;
client->port_segment = (jack_shm_info_t *)
malloc (sizeof (jack_shm_info_t) * client->n_port_types);
if ((client->port_segment = (jack_shm_info_t *) malloc (sizeof (jack_shm_info_t) * client->n_port_types)) == NULL) {
goto fail;
}
for (ptid = 0; ptid < client->n_port_types; ++ptid) {
client->port_segment[ptid].index =
@@ -2440,10 +2466,7 @@ jack_get_ports (jack_client_t *client,
psp = engine->ports;
match_cnt = 0;

matching_ports = (const char **)
malloc (sizeof (char *) * engine->port_max);

if (matching_ports == NULL) {
if ((matching_ports = (const char **) malloc (sizeof (char *) * engine->port_max)) == NULL) {
return NULL;
}



+ 5
- 3
libjack/intclient.c View File

@@ -100,12 +100,14 @@ jack_get_internal_client_name (jack_client_t *client,

jack_client_deliver_request (client, &req);

if (req.status & JackFailure)
if (req.status & JackFailure) {
return NULL;
}

/* allocate storage for returning the name */
name = malloc (strlen (req.x.intclient.name));
strcpy (name, req.x.intclient.name);
if ((name = strdup (req.x.intclient.name)) == NULL) {
return NULL;
}

return name;
}


+ 13
- 3
libjack/port.c View File

@@ -176,8 +176,12 @@ jack_port_new (const jack_client_t *client, jack_port_id_t port_id,
{
jack_port_shared_t *shared = &control->ports[port_id];
jack_port_type_id_t ptid = shared->ptype_id;
jack_port_t *port = (jack_port_t *) malloc (sizeof (jack_port_t));
jack_port_t *port;

if ((port = (jack_port_t *) malloc (sizeof (jack_port_t))) == NULL) {
return NULL;
}
port->mix_buffer = NULL;
port->client_segment_base = NULL;
port->shared = shared;
@@ -332,6 +336,11 @@ jack_port_get_connections (const jack_port_t *port)
ret = (const char **)
malloc (sizeof (char *)
* (jack_slist_length (port->connections) + 1));
if (ret == NULL) {
pthread_mutex_unlock (&((jack_port_t *)port)->connection_lock);
return NULL;
}

for (n = 0, node = port->connections; node;
node = jack_slist_next (node), ++n) {
jack_port_t* other =(jack_port_t *) node->data;
@@ -380,8 +389,9 @@ jack_port_get_all_connections (const jack_client_t *client,
return req.x.port_connections.ports;
}

ret = (const char **)
malloc (sizeof (char *) * (req.x.port_connections.nports + 1));
if ((ret = (const char **) malloc (sizeof (char *) * (req.x.port_connections.nports + 1))) == NULL) {
return NULL;
}

for (i = 0; i < req.x.port_connections.nports; ++i ) {
jack_port_id_t port_id;


+ 185
- 180
libjack/ringbuffer.c View File

@@ -35,22 +35,27 @@
jack_ringbuffer_t *
jack_ringbuffer_create (size_t sz)
{
int power_of_two;
jack_ringbuffer_t *rb;

rb = malloc (sizeof (jack_ringbuffer_t));

for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++);

rb->size = 1 << power_of_two;
rb->size_mask = rb->size;
rb->size_mask -= 1;
rb->write_ptr = 0;
rb->read_ptr = 0;
rb->buf = malloc (rb->size);
rb->mlocked = 0;

return rb;
int power_of_two;
jack_ringbuffer_t *rb;
if ((rb = malloc (sizeof (jack_ringbuffer_t))) == NULL) {
return NULL;
}
for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++);
rb->size = 1 << power_of_two;
rb->size_mask = rb->size;
rb->size_mask -= 1;
rb->write_ptr = 0;
rb->read_ptr = 0;
if ((rb->buf = malloc (rb->size)) == NULL) {
free (rb);
return NULL;
}
rb->mlocked = 0;
return rb;
}

/* Free all data associated with the ringbuffer `rb'. */
@@ -59,12 +64,12 @@ void
jack_ringbuffer_free (jack_ringbuffer_t * rb)
{
#ifdef USE_MLOCK
if (rb->mlocked) {
munlock (rb->buf, rb->size);
}
if (rb->mlocked) {
munlock (rb->buf, rb->size);
}
#endif /* USE_MLOCK */
free (rb->buf);
free (rb);
free (rb->buf);
free (rb);
}

/* Lock the data block of `rb' using the system call 'mlock'. */
@@ -73,12 +78,12 @@ int
jack_ringbuffer_mlock (jack_ringbuffer_t * rb)
{
#ifdef USE_MLOCK
if (mlock (rb->buf, rb->size)) {
return -1;
}
if (mlock (rb->buf, rb->size)) {
return -1;
}
#endif /* USE_MLOCK */
rb->mlocked = 1;
return 0;
rb->mlocked = 1;
return 0;
}

/* Reset the read and write pointers to zero. This is not thread
@@ -87,8 +92,8 @@ jack_ringbuffer_mlock (jack_ringbuffer_t * rb)
void
jack_ringbuffer_reset (jack_ringbuffer_t * rb)
{
rb->read_ptr = 0;
rb->write_ptr = 0;
rb->read_ptr = 0;
rb->write_ptr = 0;
}

/* Return the number of bytes available for reading. This is the
@@ -98,16 +103,16 @@ jack_ringbuffer_reset (jack_ringbuffer_t * rb)
size_t
jack_ringbuffer_read_space (const jack_ringbuffer_t * rb)
{
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
return w - r;
} else {
return (w - r + rb->size) & rb->size_mask;
}
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
return w - r;
} else {
return (w - r + rb->size) & rb->size_mask;
}
}

/* Return the number of bytes available for writing. This is the
@@ -117,18 +122,18 @@ jack_ringbuffer_read_space (const jack_ringbuffer_t * rb)
size_t
jack_ringbuffer_write_space (const jack_ringbuffer_t * rb)
{
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
return ((r - w + rb->size) & rb->size_mask) - 1;
} else if (w < r) {
return (r - w) - 1;
} else {
return rb->size - 1;
}
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
return ((r - w + rb->size) & rb->size_mask) - 1;
} else if (w < r) {
return (r - w) - 1;
} else {
return rb->size - 1;
}
}

/* The copying data reader. Copy at most `cnt' bytes from `rb' to
@@ -137,77 +142,77 @@ jack_ringbuffer_write_space (const jack_ringbuffer_t * rb)
size_t
jack_ringbuffer_read (jack_ringbuffer_t * rb, char *dest, size_t cnt)
{
size_t free_cnt;
size_t cnt2;
size_t to_read;
size_t n1, n2;
size_t free_cnt;
size_t cnt2;
size_t to_read;
size_t n1, n2;

if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) {
return 0;
}
if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) {
return 0;
}

to_read = cnt > free_cnt ? free_cnt : cnt;
to_read = cnt > free_cnt ? free_cnt : cnt;

cnt2 = rb->read_ptr + to_read;
cnt2 = rb->read_ptr + to_read;

if (cnt2 > rb->size) {
n1 = rb->size - rb->read_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_read;
n2 = 0;
}
if (cnt2 > rb->size) {
n1 = rb->size - rb->read_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_read;
n2 = 0;
}

memcpy (dest, &(rb->buf[rb->read_ptr]), n1);
rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;
memcpy (dest, &(rb->buf[rb->read_ptr]), n1);
rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask;

if (n2) {
memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2);
rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
}
if (n2) {
memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2);
rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask;
}

return to_read;
return to_read;
}

/* The copying data reader w/o read pointer advance. Copy at most
`cnt' bytes from `rb' to `dest'. Returns the actual number of bytes
copied. */
copied. */

size_t
jack_ringbuffer_peek (jack_ringbuffer_t * rb, char *dest, size_t cnt)
{
size_t free_cnt;
size_t cnt2;
size_t to_read;
size_t n1, n2;
size_t tmp_read_ptr;
size_t free_cnt;
size_t cnt2;
size_t to_read;
size_t n1, n2;
size_t tmp_read_ptr;

tmp_read_ptr = rb->read_ptr;
tmp_read_ptr = rb->read_ptr;

if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) {
return 0;
}
if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) {
return 0;
}

to_read = cnt > free_cnt ? free_cnt : cnt;
to_read = cnt > free_cnt ? free_cnt : cnt;

cnt2 = tmp_read_ptr + to_read;
cnt2 = tmp_read_ptr + to_read;

if (cnt2 > rb->size) {
n1 = rb->size - tmp_read_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_read;
n2 = 0;
}
if (cnt2 > rb->size) {
n1 = rb->size - tmp_read_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_read;
n2 = 0;
}

memcpy (dest, &(rb->buf[tmp_read_ptr]), n1);
tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;
memcpy (dest, &(rb->buf[tmp_read_ptr]), n1);
tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask;

if (n2) {
memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2);
}
if (n2) {
memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2);
}

return to_read;
return to_read;
}


@@ -217,36 +222,36 @@ jack_ringbuffer_peek (jack_ringbuffer_t * rb, char *dest, size_t cnt)
size_t
jack_ringbuffer_write (jack_ringbuffer_t * rb, const char *src, size_t cnt)
{
size_t free_cnt;
size_t cnt2;
size_t to_write;
size_t n1, n2;
size_t free_cnt;
size_t cnt2;
size_t to_write;
size_t n1, n2;

if ((free_cnt = jack_ringbuffer_write_space (rb)) == 0) {
return 0;
}
if ((free_cnt = jack_ringbuffer_write_space (rb)) == 0) {
return 0;
}

to_write = cnt > free_cnt ? free_cnt : cnt;
to_write = cnt > free_cnt ? free_cnt : cnt;

cnt2 = rb->write_ptr + to_write;
cnt2 = rb->write_ptr + to_write;

if (cnt2 > rb->size) {
n1 = rb->size - rb->write_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_write;
n2 = 0;
}
if (cnt2 > rb->size) {
n1 = rb->size - rb->write_ptr;
n2 = cnt2 & rb->size_mask;
} else {
n1 = to_write;
n2 = 0;
}

memcpy (&(rb->buf[rb->write_ptr]), src, n1);
rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;
memcpy (&(rb->buf[rb->write_ptr]), src, n1);
rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask;

if (n2) {
memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2);
rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
}
if (n2) {
memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2);
rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask;
}

return to_write;
return to_write;
}

/* Advance the read pointer `cnt' places. */
@@ -254,8 +259,8 @@ jack_ringbuffer_write (jack_ringbuffer_t * rb, const char *src, size_t cnt)
void
jack_ringbuffer_read_advance (jack_ringbuffer_t * rb, size_t cnt)
{
size_t tmp = (rb->read_ptr + cnt) & rb->size_mask;
rb->read_ptr = tmp;
size_t tmp = (rb->read_ptr + cnt) & rb->size_mask;
rb->read_ptr = tmp;
}

/* Advance the write pointer `cnt' places. */
@@ -263,8 +268,8 @@ jack_ringbuffer_read_advance (jack_ringbuffer_t * rb, size_t cnt)
void
jack_ringbuffer_write_advance (jack_ringbuffer_t * rb, size_t cnt)
{
size_t tmp = (rb->write_ptr + cnt) & rb->size_mask;
rb->write_ptr = tmp;
size_t tmp = (rb->write_ptr + cnt) & rb->size_mask;
rb->write_ptr = tmp;
}

/* The non-copying data reader. `vec' is an array of two places. Set
@@ -276,39 +281,39 @@ void
jack_ringbuffer_get_read_vector (const jack_ringbuffer_t * rb,
jack_ringbuffer_data_t * vec)
{
size_t free_cnt;
size_t cnt2;
size_t w, r;
size_t free_cnt;
size_t cnt2;
size_t w, r;

w = rb->write_ptr;
r = rb->read_ptr;
w = rb->write_ptr;
r = rb->read_ptr;

if (w > r) {
free_cnt = w - r;
} else {
free_cnt = (w - r + rb->size) & rb->size_mask;
}
if (w > r) {
free_cnt = w - r;
} else {
free_cnt = (w - r + rb->size) & rb->size_mask;
}

cnt2 = r + free_cnt;
cnt2 = r + free_cnt;

if (cnt2 > rb->size) {
if (cnt2 > rb->size) {

/* Two part vector: the rest of the buffer after the current write
ptr, plus some from the start of the buffer. */
/* Two part vector: the rest of the buffer after the current write
ptr, plus some from the start of the buffer. */

vec[0].buf = &(rb->buf[r]);
vec[0].len = rb->size - r;
vec[1].buf = rb->buf;
vec[1].len = cnt2 & rb->size_mask;
vec[0].buf = &(rb->buf[r]);
vec[0].len = rb->size - r;
vec[1].buf = rb->buf;
vec[1].len = cnt2 & rb->size_mask;

} else {
} else {

/* Single part vector: just the rest of the buffer */
/* Single part vector: just the rest of the buffer */

vec[0].buf = &(rb->buf[r]);
vec[0].len = free_cnt;
vec[1].len = 0;
}
vec[0].buf = &(rb->buf[r]);
vec[0].len = free_cnt;
vec[1].len = 0;
}
}

/* The non-copying data writer. `vec' is an array of two places. Set
@@ -320,35 +325,35 @@ void
jack_ringbuffer_get_write_vector (const jack_ringbuffer_t * rb,
jack_ringbuffer_data_t * vec)
{
size_t free_cnt;
size_t cnt2;
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
free_cnt = ((r - w + rb->size) & rb->size_mask) - 1;
} else if (w < r) {
free_cnt = (r - w) - 1;
} else {
free_cnt = rb->size - 1;
}
cnt2 = w + free_cnt;
if (cnt2 > rb->size) {
/* Two part vector: the rest of the buffer after the current write
ptr, plus some from the start of the buffer. */
vec[0].buf = &(rb->buf[w]);
vec[0].len = rb->size - w;
vec[1].buf = rb->buf;
vec[1].len = cnt2 & rb->size_mask;
} else {
vec[0].buf = &(rb->buf[w]);
vec[0].len = free_cnt;
vec[1].len = 0;
}
size_t free_cnt;
size_t cnt2;
size_t w, r;
w = rb->write_ptr;
r = rb->read_ptr;
if (w > r) {
free_cnt = ((r - w + rb->size) & rb->size_mask) - 1;
} else if (w < r) {
free_cnt = (r - w) - 1;
} else {
free_cnt = rb->size - 1;
}
cnt2 = w + free_cnt;
if (cnt2 > rb->size) {
/* Two part vector: the rest of the buffer after the current write
ptr, plus some from the start of the buffer. */
vec[0].buf = &(rb->buf[w]);
vec[0].len = rb->size - w;
vec[1].buf = rb->buf;
vec[1].len = cnt2 & rb->size_mask;
} else {
vec[0].buf = &(rb->buf[w]);
vec[0].len = free_cnt;
vec[1].len = 0;
}
}

+ 3
- 1
libjack/thread.c View File

@@ -176,7 +176,9 @@ jack_client_create_thread (jack_client_t* client,
return result;
}

thread_args = (jack_thread_arg_t *) malloc (sizeof (jack_thread_arg_t));
if ((thread_args = (jack_thread_arg_t *) malloc (sizeof (jack_thread_arg_t))) == NULL) {
return -1;
}

thread_args->client = client;
thread_args->work_function = start_routine;


Loading…
Cancel
Save