tpool.h

// https://nachtimwald.com/2019/04/12/thread-pool-in-c/

#ifndef ARP_TEST_TPOOL_H
#define ARP_TEST_TPOOL_H

#ifndef size_t
typedef unsigned long size_t;
#endif

#ifndef bool
typedef int bool;
#endif

struct tpool;
typedef struct tpool tpool_t;

typedef void (*thread_func_t)(void *arg);

tpool_t *tpool_create(size_t num);
void tpool_destroy(tpool_t *tm);

bool tpool_add_work(tpool_t *tm, thread_func_t func, void *arg);
void tpool_wait(tpool_t *tm);

#endif //ARP_TEST_TPOOL_H

* tpool.c

#include <pthread.h>
#include <stdlib.h>  /* malloc, free */
#include "tpool.h"

struct tpool_work {
    thread_func_t      func;
    void              *arg;
    struct tpool_work *next;
};
typedef struct tpool_work tpool_work_t;

struct tpool {
    tpool_work_t    *work_first;
    tpool_work_t    *work_last;
    pthread_mutex_t  work_mutex;
    pthread_cond_t   work_cond;
    pthread_cond_t   working_cond;
    size_t           working_cnt;
    size_t           thread_cnt;
    bool             stop;
};

static tpool_work_t *tpool_work_create(thread_func_t func, void *arg) {
    tpool_work_t *work;

    if (func == NULL)
        return NULL;

    work       = malloc(sizeof(*work));
    work->func = func;
    work->arg  = arg;
    work->next = NULL;
    return work;
}

static void tpool_work_destroy(tpool_work_t *work) {
    if (work == NULL)
        return;
    free(work);
}


static tpool_work_t *tpool_work_get(tpool_t *tm) {
    tpool_work_t *work;

    if (tm == NULL)
        return NULL;

    work = tm->work_first;
    if (work == NULL)
        return NULL;

    if (work->next == NULL) {
        tm->work_first = NULL;
        tm->work_last  = NULL;
    } else {
        tm->work_first = work->next;
    }

    return work;
}

static void *tpool_worker(void *arg) {
    tpool_t      *tm = arg;
    tpool_work_t *work;

    while (1) {
        pthread_mutex_lock(&tm->work_mutex);

        while (tm->work_first == NULL && !tm->stop) {
            pthread_cond_wait(&tm->work_cond, &tm->work_mutex);
        }
        if (tm->stop) {
            break;
        }
        work = tpool_work_get(tm);
        tm->working_cnt++;
        pthread_mutex_unlock(&tm->work_mutex);

        if (work != NULL) {
            work->func(work->arg);
            tpool_work_destroy(work);
        }
        pthread_mutex_lock(&tm->work_mutex);
        tm->working_cnt--;
        if (!tm->stop && tm->working_cnt == 0 && tm->work_first == NULL) {
            pthread_cond_signal(&tm->working_cond);
        }
        pthread_mutex_unlock(&tm->work_mutex);
    }
    tm->thread_cnt--;
    pthread_cond_signal(&tm->working_cond);
    pthread_mutex_unlock(&tm->work_mutex);
    return NULL;
}

tpool_t *tpool_create(size_t num) {
    tpool_t   *tm;
    pthread_t  thread;
    size_t     i;

    if (num == 0)
        num = 2;

    tm             = calloc(1, sizeof(*tm));
    tm->thread_cnt = num;

    pthread_mutex_init(&tm->work_mutex, NULL);
    pthread_cond_init(&tm->work_cond, NULL);
    pthread_cond_init(&tm->working_cond, NULL);

    tm->work_first = NULL;
    tm->work_last  = NULL;

    for (i=0; i<num; i++) {
        pthread_create(&thread, NULL, tpool_worker, tm);
        pthread_detach(thread);
    }

    return tm;
}

#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif

void tpool_destroy(tpool_t *tm)
{
    tpool_work_t *work;
    tpool_work_t *work2;

    if (tm == NULL)
        return;

    pthread_mutex_lock(&tm->work_mutex);
    work = tm->work_first;
    while (work != NULL) {
        work2 = work->next;
        tpool_work_destroy(work);
        work = work2;
    }
    tm->stop = true;
    pthread_cond_broadcast(&tm->work_cond);
    pthread_mutex_unlock(&tm->work_mutex);

    tpool_wait(tm);

    pthread_mutex_destroy(&tm->work_mutex);
    pthread_cond_destroy(&tm->work_cond);
    pthread_cond_destroy(&tm->working_cond);

    free(tm);
}

bool tpool_add_work(tpool_t *tm, thread_func_t func, void *arg)
{
    tpool_work_t *work;

    if (tm == NULL) {
        return false;
    }
    work = tpool_work_create(func, arg);
    if (work == NULL) {
        return false;
    }
    pthread_mutex_lock(&tm->work_mutex);
    if (tm->work_first == NULL) {
        tm->work_first = work;
        tm->work_last  = tm->work_first;
    } else {
        tm->work_last->next = work;
        tm->work_last       = work;
    }

    pthread_cond_broadcast(&tm->work_cond);
    pthread_mutex_unlock(&tm->work_mutex);

    return true;
}

void tpool_wait(tpool_t *tm)
{
    if (tm == NULL)
        return;

    pthread_mutex_lock(&tm->work_mutex);
    while ((!tm->stop && tm->working_cnt != 0) || (tm->stop && tm->thread_cnt != 0)) {
        pthread_cond_wait(&tm->working_cond, &tm->work_mutex);
    }
    pthread_mutex_unlock(&tm->work_mutex);
}

Usage:

typedef struct {
    char path_dst[256];
    char path_src[256];
    char ip[16];
} save_response_t;

void https_worker(void *param) {
    save_response_t *args = (save_response_t *)param;
    https_get_body(args->ip, args->path_src, args->path_dst);
}

int main(int argc, char *argv[]) {
    tpool_t *tm;
    int i;
    save_response_t thread_args;
    /* ... */
    strcpy(thread_args.path_dst, path_dst);
    strcpy(thread_args.path_src, path_src);
    strcpy(thread_args.ip, ip);

    /* @ref: https://nachtimwald.com/2019/04/12/thread-pool-in-c/ */
    tm = tpool_create(4);
    for (i = 0; i < 1048576; i++) {
        tpool_add_work(tm, https_worker, &thread_args);
    }
    tpool_wait(tm);
    tpool_destroy(tm);
    return 0;
}

* https.h

#ifndef ARP_TEST_HTTPS_H
#define ARP_TEST_HTTPS_H

int https_get_body(char *ip, char *in_path, char *out_path);

#endif //ARP_TEST_HTTPS_H

* https.c

/*
 * @ref: https://aticleworld.com/ssl-server-client-using-openssl-in-c/
 * sudo apt-get install libssl–dev
 * -L/path/to/ssl_dir -lssl -lcrypto
 */
#include <string.h> /* memset */
#include "arclog.h" /* bclerreg, E_FAIL */
#include "bcl_socket.h"
#include "https.h"

#include <openssl/ssl.h> /* SSL_Library_init() */
#include <openssl/err.h>

extern unsigned int ip2int(const char *ip);

static SSL_CTX *InitCTX(void) {
    SSL_METHOD *method;
    SSL_CTX *ctx;
    OpenSSL_add_all_algorithms();  /* Load cryptos, et.al. */
    SSL_load_error_strings();   /* Bring in and register error messages */
    method = TLSv1_2_client_method();  /* Create new client-method instance */
    ctx = SSL_CTX_new(method);   /* Create new context */
    if ( ctx == NULL ) {
        ERR_print_errors_fp(stderr);
        abort();
    }
    return ctx;
}

ssize_t send_from_file_ssl(SSL *ssl, char *path) {
    byte_t *buf = NULL;
    size_t isz = 0;
    ssize_t outBytes;

    buf = file_get_contents(path, &isz);
    /* bcl_xxd(buf, isz); */
    outBytes = SSL_write(ssl, buf, isz);
    /* bcl_xxd(buf, isz); */
    free(buf);
    return outBytes;
}

int https_get_body(char *ip, char *in_path, char *out_path) {
    SSL_CTX *ctx = NULL;
    SSL *ssl;
    int bytes;
#define HTTPS_BUFF_SIZE 4096
    char buf[HTTPS_BUFF_SIZE];
    int sockfd = -1;
    FILE *out = NULL;
    char *body = NULL;

    SSL_library_init();
    ctx = InitCTX();
    ssl = SSL_new(ctx);      /* create new SSL connection state */
    sockfd = bcl_tcp_connector(ip2int(ip), 443);
    SSL_set_fd(ssl, sockfd);    /* attach the socket descriptor */
    if ( SSL_connect(ssl) < 0 ) {  /* perform the connection */
        ERR_print_errors_fp(stderr);
        return E_FAIL;
    }
    send_from_file_ssl(ssl, in_path);
    bytes = SSL_read(ssl, buf, HTTPS_BUFF_SIZE);
    /* Dump HTTP header */
    bcl_xxd(buf, bytes);

    out = fopen(out_path, "w");
    if (NULL == out) {
        bclerreg(E_OSCALL, _FL_, "fopen(%s)", out_path);
        return E_FAIL;
    }
    if (NULL != (body = strstr(buf, "\r\n\r\n"))) {
        body += 4;
        fwrite(body, 1, strlen(body), out);
    }
    while (0<(bytes = SSL_read(ssl, buf, HTTPS_BUFF_SIZE))) {
        fwrite(buf, 1, bytes, out);
        memset(buf, 0, bytes);
    }
    fclose(out);
    bcl_closesock(sockfd);

    SSL_free(ssl);
    SSL_CTX_free(ctx);
    return E_OK;
}

/mnt/e/CLionProjects/arp/fap30/print/sms.txt

POST /index.php/api/sendCode?phone=131****9242&type=1 HTTP/1.1
Host: enjoysoft.021city.cn
Accept: application/json
Connection: Closed
X-Forwared-For: 121.5.101.32

C:\Windows\system32\wsl.exe --distribution Ubuntu --exec /bin/bash -c "export ESWTDIR=/mnt/e/CLionProjects/arp && export FAPWORKDIR=/mnt/e/CLionProjects/arp/fap30 && cd /mnt/e/CLionProjects/arp/fap30 && /mnt/e/CLionProjects/arp/cmake-build-debug/arp_test ./input/banner.txt banner_3.jpg"
ip=121.5.101.32
path_dst=/mnt/e/CLionProjects/arp/fap30/print/sms.txt
00000000: 4854 5450 2f31 2e31 2032 3030 204f 4b0d  HTTP/1.1 200 OK.
00000010: 0a53 6572 7665 723a 206e 6769 6e78 0d0a  .Server: nginx..
00000020: 436f 6e74 656e 742d 5479 7065 3a20 6170  Content-Type: ap
00000030: 706c 6963 6174 696f 6e2f 6a73 6f6e 0d0a  plication/json..
00000040: 5472 616e 7366 6572 2d45 6e63 6f64 696e  Transfer-Encodin
00000050: 673a 2063 6875 6e6b 6564 0d0a 436f 6e6e  g: chunked..Conn
00000060: 6563 7469 6f6e 3a20 636c 6f73 650d 0a43  ection: close..C
00000070: 6163 6865 2d43 6f6e 7472 6f6c 3a20 6e6f  ache-Control: no
00000080: 2d63 6163 6865 2c20 7072 6976 6174 650d  -cache, private.
00000090: 0a44 6174 653a 204d 6f6e 2c20 3330 204f  .Date: Mon, 30 O
000000a0: 6374 2032 3032 3320 3032 3a30 313a 3437  ct 2023 02:01:47
000000b0: 2047 4d54 0d0a 582d 5261 7465 4c69 6d69   GMT..X-RateLimi
000000c0: 742d 4c69 6d69 743a 2036 300d 0a58 2d52  t-Limit: 60..X-R
000000d0: 6174 654c 696d 6974 2d52 656d 6169 6e69  ateLimit-Remaini
000000e0: 6e67 3a20 3536 0d0a 4163 6365 7373 2d43  ng: 56..Access-C
000000f0: 6f6e 7472 6f6c 2d41 6c6c 6f77 2d4f 7269  ontrol-Allow-Ori
00000100: 6769 6e3a 202a 0d0a 5374 7269 6374 2d54  gin: *..Strict-T
00000110: 7261 6e73 706f 7274 2d53 6563 7572 6974  ransport-Securit
00000120: 793a 206d 6178 2d61 6765 3d33 3135 3336  y: max-age=31536
00000130: 3030 300d 0a0d 0a61 380d 0a7b 226d 6573  000....a8..{"mes
00000140: 7361 6765 223a 2266 6169 6c65 6422 2c22  sage":"failed","
00000150: 636f 6465 223a 3130 3031 2c22 7265 7375  code":1001,"resu
00000160: 6c74 223a 7b22 6d73 6722 3a22 7468 6520  lt":{"msg":"the
00000170: 6e75 6d62 6572 206f 6620 736d 7320 6d65  number of sms me
00000180: 7373 6167 6573 2073 656e 7420 6672 6f6d  ssages sent from
00000190: 2061 2073 696e 676c 6520 6d6f 6269 6c65   a single mobile
000001a0: 206e 756d 6265 7220 6576 6572 7920 6461   number every da
000001b0: 7920 6578 6365 6564 7320 7468 6520 7570  y exceeds the up
000001c0: 7065 7220 6c69 6d69 7422 7d2c 2274 696d  per limit"},"tim
000001d0: 6573 7461 6d70 223a 3136 3938 3633 3133  estamp":16986313
000001e0: 3037 7d0d 0a30 0d0a 0d0a                 07}..0....

Efficient C String Builder

C thread pool 线程池, C StringBuilder_bc