diff --git a/make_libmodbus.sh b/make_libmodbus.sh index 9f9fa3c..9e7a4fc 100755 --- a/make_libmodbus.sh +++ b/make_libmodbus.sh @@ -7,7 +7,7 @@ cd ./libmodbus/ # reset to patch commit number git reset --hard 79a7c1ea82e5bb2e13c8efc77cb3e8a6d7368c58 -patch -c -p2 < ../mt.patch +patch -p0 < ../mt.patch #./autogen.sh && ./configure --enable-static=yes --enable-shared=no && make ./autogen.sh && ./configure && make diff --git a/modbus.js b/modbus.js index 7dc307e..2fdc9a3 100644 --- a/modbus.js +++ b/modbus.js @@ -997,7 +997,7 @@ function createMasterRtu(a, con, cbs) { api.destroy = function () { isWorking = false; - mb.close(ctx); + mb.close_mt(ctx); mb.free(ctx); ctx = null; diff --git a/mt.patch b/mt.patch index 835e8fc..7ebcb3d 100644 --- a/mt.patch +++ b/mt.patch @@ -1,368 +1,313 @@ -diff -cr -x .git ./libmodbus.orig/src/modbus.c ./libmodbus/src/modbus.c -*** ./libmodbus.orig/src/modbus.c 2014-02-07 22:46:00.000000000 +0100 ---- ./libmodbus/src/modbus.c 2014-02-07 22:46:48.000000000 +0100 -*************** -*** 1457,1468 **** ---- 1457,1477 ---- - ctx->backend->close(ctx); - } - -+ void modbus_close_mt(modbus_t *ctx) -+ { -+ if (ctx == NULL) -+ return; -+ -+ ctx->backend->close_mt(ctx); -+ } -+ - void modbus_free(modbus_t *ctx) - { - if (ctx == NULL) - return; - - free(ctx->backend_data); -+ free(ctx->mt_data); - free(ctx); - } - -diff -cr -x .git ./libmodbus.orig/src/modbus.h ./libmodbus/src/modbus.h -*** ./libmodbus.orig/src/modbus.h 2014-02-07 22:46:00.000000000 +0100 ---- ./libmodbus/src/modbus.h 2014-02-07 22:46:48.000000000 +0100 -*************** -*** 169,174 **** ---- 169,175 ---- - - EXPORT int modbus_connect(modbus_t *ctx); - EXPORT void modbus_close(modbus_t *ctx); -+ EXPORT void modbus_close_mt(modbus_t *ctx); - - EXPORT void modbus_free(modbus_t *ctx); - -diff -cr -x .git ./libmodbus.orig/src/modbus-private.h ./libmodbus/src/modbus-private.h -*** ./libmodbus.orig/src/modbus-private.h 2014-02-07 22:46:00.000000000 +0100 ---- ./libmodbus/src/modbus-private.h 2014-02-07 22:46:48.000000000 +0100 -*************** -*** 109,114 **** ---- 109,115 ---- - const uint8_t *rsp, int rsp_length); - int (*connect) (modbus_t *ctx); - void (*close) (modbus_t *ctx); -+ void (*close_mt) (modbus_t *ctx); - int (*flush) (modbus_t *ctx); - int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length); - } modbus_backend_t; -*************** -*** 124,129 **** ---- 125,131 ---- - struct timeval byte_timeout; - const modbus_backend_t *backend; - void *backend_data; -+ void *mt_data; - }; - - void _modbus_init_common(modbus_t *ctx); -diff -cr -x .git ./libmodbus.orig/src/modbus-rtu.c ./libmodbus/src/modbus-rtu.c -*** ./libmodbus.orig/src/modbus-rtu.c 2014-02-07 22:46:00.000000000 +0100 ---- ./libmodbus/src/modbus-rtu.c 2014-02-07 22:58:00.000000000 +0100 -*************** -*** 937,942 **** ---- 937,954 ---- - #endif - } - -+ void _modbus_rtu_close_mt(modbus_t *ctx) -+ { -+ ssize_t size; -+ _modbus_rtu_close(ctx); -+ -+ modbus_rtu_mt_t *ctx_mt = ctx->mt_data; -+ size = write(ctx_mt->mtp_w, "q", 1); -+ if (size == -1) -+ fprintf(stderr, "ERROR returned by write() (%s)\n", -+ strerror(errno)); -+ } -+ - int _modbus_rtu_flush(modbus_t *ctx) - { - #if defined(_WIN32) -*************** -*** 964,970 **** - return -1; - } - #else -! while ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) { - if (errno == EINTR) { - if (ctx->debug) { - fprintf(stderr, "A non blocked signal was caught\n"); ---- 976,988 ---- - return -1; - } - #else -! /* Add pipe descriptor to select */ -! modbus_rtu_mt_t *ctx_mt = ctx->mt_data; -! FD_SET(ctx_mt->mtp_r, rset); -! -! int max_n = (ctx->s > ctx_mt->mtp_r) ? ctx->s : ctx_mt->mtp_r; -! -! while ((s_rc = select(max_n+1, rset, NULL, NULL, tv)) == -1) { - if (errno == EINTR) { - if (ctx->debug) { - fprintf(stderr, "A non blocked signal was caught\n"); -*************** -*** 972,977 **** ---- 990,996 ---- - /* Necessary after an error */ - FD_ZERO(rset); - FD_SET(ctx->s, rset); -+ FD_SET(ctx_mt->mtp_r, rset); - } else { - return -1; - } -*************** -*** 983,989 **** - return -1; - } - #endif -! - return s_rc; - } - ---- 1002,1013 ---- - return -1; - } - #endif -! if (FD_ISSET(ctx_mt->mtp_r, rset)) { -! /* Connection reset. */ -! errno = ECONNRESET; -! return -1; -! } -! - return s_rc; - } - -*************** -*** 1004,1009 **** ---- 1028,1034 ---- - _modbus_rtu_pre_check_confirmation, - _modbus_rtu_connect, - _modbus_rtu_close, -+ _modbus_rtu_close_mt, - _modbus_rtu_flush, - _modbus_rtu_select - }; -*************** -*** 1014,1019 **** ---- 1039,1045 ---- - { - modbus_t *ctx; - modbus_rtu_t *ctx_rtu; -+ modbus_rtu_mt_t *ctx_mt; - size_t dest_size; - size_t ret_size; - -*************** -*** 1062,1067 **** ---- 1088,1107 ---- - #endif - - ctx_rtu->confirmation_to_ignore = FALSE; -+ -+ /* Create pipe chanel */ -+ ctx->mt_data = (modbus_rtu_mt_t *) malloc(sizeof(modbus_rtu_mt_t)); -+ ctx_mt = (modbus_rtu_mt_t *)ctx->mt_data; -+ -+ int mtp[2]; -+ if (pipe(mtp) == -1) { -+ fprintf(stderr, "ERROR Can't create pipe (%s)\n", -+ strerror(errno)); -+ modbus_free(ctx); -+ return NULL; -+ } -+ ctx_mt->mtp_r = mtp[0]; -+ ctx_mt->mtp_w = mtp[1]; - - return ctx; - } -diff -cr -x .git ./libmodbus.orig/src/modbus-rtu-private.h ./libmodbus/src/modbus-rtu-private.h -*** ./libmodbus.orig/src/modbus-rtu-private.h 2014-02-07 22:46:00.000000000 +0100 ---- ./libmodbus/src/modbus-rtu-private.h 2014-02-07 22:46:48.000000000 +0100 -*************** -*** 94,97 **** ---- 94,103 ---- - int confirmation_to_ignore; - } modbus_rtu_t; - -+ typedef struct _modbus_rtu_mt { -+ /* Pipes for control in multithread */ -+ int mtp_r; -+ int mtp_w; -+ } modbus_rtu_mt_t; -+ - #endif /* _MODBUS_RTU_PRIVATE_H_ */ -diff -cr -x .git ./libmodbus.orig/src/modbus-tcp.c ./libmodbus/src/modbus-tcp.c -*** ./libmodbus.orig/src/modbus-tcp.c 2014-02-07 22:46:00.000000000 +0100 ---- ./libmodbus/src/modbus-tcp.c 2014-02-07 22:46:48.000000000 +0100 -*************** -*** 404,409 **** ---- 404,420 ---- - close(ctx->s); - } - -+ void _modbus_tcp_close_mt(modbus_t *ctx) -+ { -+ _modbus_tcp_close(ctx); -+ -+ modbus_tcp_mt_t *ctx_mt = ctx->mt_data; -+ if (ctx_mt->lst_socket != -1) { -+ shutdown(ctx_mt->lst_socket, SHUT_RDWR); -+ close(ctx_mt->lst_socket); -+ } -+ } -+ - int _modbus_tcp_flush(modbus_t *ctx) - { - int rc; -*************** -*** 448,453 **** ---- 459,465 ---- - int yes; - struct sockaddr_in addr; - modbus_tcp_t *ctx_tcp = ctx->backend_data; -+ modbus_tcp_mt_t *ctx_mt = ctx->mt_data; - - #ifdef OS_WIN32 - if (_modbus_tcp_init_win32() == -1) { -*************** -*** 481,486 **** ---- 493,500 ---- - close(new_socket); - return -1; - } -+ -+ ctx_mt->lst_socket = new_socket; - - return new_socket; - } -*************** -*** 495,500 **** ---- 509,515 ---- - const char *service; - int new_socket; - modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; -+ modbus_tcp_mt_t *ctx_mt = ctx->mt_data; - - if (ctx_tcp_pi->node[0] == 0) - node = NULL; /* == any */ -*************** -*** 577,582 **** ---- 592,599 ---- - if (new_socket < 0) { - return -1; - } -+ -+ ctx_mt->lst_socket = new_socket; - - return new_socket; - } -*************** -*** 671,676 **** ---- 688,694 ---- - _modbus_tcp_pre_check_confirmation, - _modbus_tcp_connect, - _modbus_tcp_close, -+ _modbus_tcp_close_mt, - _modbus_tcp_flush, - _modbus_tcp_select - }; -*************** -*** 693,698 **** ---- 711,717 ---- - _modbus_tcp_pre_check_confirmation, - _modbus_tcp_pi_connect, - _modbus_tcp_close, -+ _modbus_tcp_close_mt, - _modbus_tcp_flush, - _modbus_tcp_select - }; -*************** -*** 701,706 **** ---- 720,726 ---- - { - modbus_t *ctx; - modbus_tcp_t *ctx_tcp; -+ modbus_tcp_mt_t *ctx_mt; - size_t dest_size; - size_t ret_size; - -*************** -*** 727,732 **** ---- 747,755 ---- - - ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); - ctx_tcp = (modbus_tcp_t *)ctx->backend_data; -+ -+ ctx->mt_data = (modbus_tcp_mt_t *) malloc(sizeof(modbus_tcp_mt_t)); -+ ctx_mt = (modbus_tcp_mt_t *)ctx->mt_data; - - dest_size = sizeof(char) * 16; - ret_size = strlcpy(ctx_tcp->ip, ip, dest_size); -*************** -*** 746,751 **** ---- 769,776 ---- - - ctx_tcp->port = port; - ctx_tcp->t_id = 0; -+ -+ ctx_mt->lst_socket = -1; - - return ctx; - } -*************** -*** 755,760 **** ---- 780,786 ---- - { - modbus_t *ctx; - modbus_tcp_pi_t *ctx_tcp_pi; -+ modbus_tcp_mt_t *ctx_mt; - size_t dest_size; - size_t ret_size; - -*************** -*** 768,773 **** ---- 794,802 ---- - - ctx->backend_data = (modbus_tcp_pi_t *) malloc(sizeof(modbus_tcp_pi_t)); - ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data; -+ -+ ctx->mt_data = (modbus_tcp_mt_t *) malloc(sizeof(modbus_tcp_mt_t)); -+ ctx_mt = (modbus_tcp_mt_t *)ctx->mt_data; - - dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH; - ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size); -*************** -*** 802,807 **** ---- 831,838 ---- - } - - ctx_tcp_pi->t_id = 0; -+ -+ ctx_mt->lst_socket = -1; - - return ctx; - } -diff -cr -x .git ./libmodbus.orig/src/modbus-tcp-private.h ./libmodbus/src/modbus-tcp-private.h -*** ./libmodbus.orig/src/modbus-tcp-private.h 2014-02-07 22:46:00.000000000 +0100 ---- ./libmodbus/src/modbus-tcp-private.h 2014-02-07 22:46:48.000000000 +0100 -*************** -*** 53,56 **** ---- 53,62 ---- - char service[_MODBUS_TCP_PI_SERVICE_LENGTH]; - } modbus_tcp_pi_t; - -+ -+ typedef struct _modbus_tcp_mt { -+ /* Main listen socket */ -+ int lst_socket; -+ } modbus_tcp_mt_t; -+ - #endif /* _MODBUS_TCP_PRIVATE_H_ */ +diff --git src/modbus-private.h src/modbus-private.h +index 7994459..799eccd 100644 +--- src/modbus-private.h ++++ src/modbus-private.h +@@ -109,6 +109,7 @@ typedef struct _modbus_backend { + const uint8_t *rsp, int rsp_length); + int (*connect) (modbus_t *ctx); + void (*close) (modbus_t *ctx); ++ void (*close_mt) (modbus_t *ctx); + int (*flush) (modbus_t *ctx); + int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length); + } modbus_backend_t; +@@ -124,6 +125,7 @@ struct _modbus { + struct timeval byte_timeout; + const modbus_backend_t *backend; + void *backend_data; ++ void *mt_data; + }; + + void _modbus_init_common(modbus_t *ctx); +diff --git src/modbus-rtu-private.h src/modbus-rtu-private.h +index c8fdeab..4f20db4 100644 +--- src/modbus-rtu-private.h ++++ src/modbus-rtu-private.h +@@ -94,4 +94,10 @@ typedef struct _modbus_rtu { + int confirmation_to_ignore; + } modbus_rtu_t; + ++typedef struct _modbus_rtu_mt { ++ /* Pipes for control in multithread */ ++ int mtp_r; ++ int mtp_w; ++} modbus_rtu_mt_t; ++ + #endif /* _MODBUS_RTU_PRIVATE_H_ */ +diff --git src/modbus-rtu.c src/modbus-rtu.c +index c41110e..60e7456 100644 +--- src/modbus-rtu.c ++++ src/modbus-rtu.c +@@ -937,6 +937,20 @@ void _modbus_rtu_close(modbus_t *ctx) + #endif + } + ++void _modbus_rtu_close_mt(modbus_t *ctx) ++{ ++ ssize_t size; ++ _modbus_rtu_close(ctx); ++ ++ modbus_rtu_mt_t *ctx_mt = ctx->mt_data; ++ size = write(ctx_mt->mtp_w, "q", 1); ++ if (size == -1) ++ fprintf(stderr, "ERROR returned by write() (%s)\n", ++ strerror(errno)); ++ close(ctx_mt->mtp_w); ++ close(ctx_mt->mtp_r); ++} ++ + int _modbus_rtu_flush(modbus_t *ctx) + { + #if defined(_WIN32) +@@ -964,7 +978,13 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rset, + return -1; + } + #else +- while ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) { ++ /* Add pipe descriptor to select */ ++ modbus_rtu_mt_t *ctx_mt = ctx->mt_data; ++ FD_SET(ctx_mt->mtp_r, rset); ++ ++ int max_n = (ctx->s > ctx_mt->mtp_r) ? ctx->s : ctx_mt->mtp_r; ++ ++ while ((s_rc = select(max_n+1, rset, NULL, NULL, tv)) == -1) { + if (errno == EINTR) { + if (ctx->debug) { + fprintf(stderr, "A non blocked signal was caught\n"); +@@ -972,6 +992,7 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rset, + /* Necessary after an error */ + FD_ZERO(rset); + FD_SET(ctx->s, rset); ++ FD_SET(ctx_mt->mtp_r, rset); + } else { + return -1; + } +@@ -983,7 +1004,12 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rset, + return -1; + } + #endif +- ++ if (FD_ISSET(ctx_mt->mtp_r, rset)) { ++ /* Connection reset. */ ++ errno = ECONNRESET; ++ return -1; ++ } ++ + return s_rc; + } + +@@ -1004,6 +1030,7 @@ const modbus_backend_t _modbus_rtu_backend = { + _modbus_rtu_pre_check_confirmation, + _modbus_rtu_connect, + _modbus_rtu_close, ++ _modbus_rtu_close_mt, + _modbus_rtu_flush, + _modbus_rtu_select + }; +@@ -1014,6 +1041,7 @@ modbus_t* modbus_new_rtu(const char *device, + { + modbus_t *ctx; + modbus_rtu_t *ctx_rtu; ++ modbus_rtu_mt_t *ctx_mt; + size_t dest_size; + size_t ret_size; + +@@ -1062,6 +1090,20 @@ modbus_t* modbus_new_rtu(const char *device, + #endif + + ctx_rtu->confirmation_to_ignore = FALSE; ++ ++ /* Create pipe chanel */ ++ ctx->mt_data = (modbus_rtu_mt_t *) malloc(sizeof(modbus_rtu_mt_t)); ++ ctx_mt = (modbus_rtu_mt_t *)ctx->mt_data; ++ ++ int mtp[2]; ++ if (pipe(mtp) == -1) { ++ fprintf(stderr, "ERROR Can't create pipe (%s)\n", ++ strerror(errno)); ++ modbus_free(ctx); ++ return NULL; ++ } ++ ctx_mt->mtp_r = mtp[0]; ++ ctx_mt->mtp_w = mtp[1]; + + return ctx; + } +diff --git src/modbus-tcp-private.h src/modbus-tcp-private.h +index 408d08a..8a2b43b 100644 +--- src/modbus-tcp-private.h ++++ src/modbus-tcp-private.h +@@ -53,4 +53,10 @@ typedef struct _modbus_tcp_pi { + char service[_MODBUS_TCP_PI_SERVICE_LENGTH]; + } modbus_tcp_pi_t; + ++ ++typedef struct _modbus_tcp_mt { ++ /* Main listen socket */ ++ int lst_socket; ++} modbus_tcp_mt_t; ++ + #endif /* _MODBUS_TCP_PRIVATE_H_ */ +diff --git src/modbus-tcp.c src/modbus-tcp.c +index b3eae81..928b850 100644 +--- src/modbus-tcp.c ++++ src/modbus-tcp.c +@@ -404,6 +404,17 @@ void _modbus_tcp_close(modbus_t *ctx) + close(ctx->s); + } + ++void _modbus_tcp_close_mt(modbus_t *ctx) ++{ ++ _modbus_tcp_close(ctx); ++ ++ modbus_tcp_mt_t *ctx_mt = ctx->mt_data; ++ if (ctx_mt->lst_socket != -1) { ++ shutdown(ctx_mt->lst_socket, SHUT_RDWR); ++ close(ctx_mt->lst_socket); ++ } ++} ++ + int _modbus_tcp_flush(modbus_t *ctx) + { + int rc; +@@ -448,6 +459,7 @@ int modbus_tcp_listen(modbus_t *ctx, int nb_connection) + int yes; + struct sockaddr_in addr; + modbus_tcp_t *ctx_tcp = ctx->backend_data; ++ modbus_tcp_mt_t *ctx_mt = ctx->mt_data; + + #ifdef OS_WIN32 + if (_modbus_tcp_init_win32() == -1) { +@@ -481,6 +493,8 @@ int modbus_tcp_listen(modbus_t *ctx, int nb_connection) + close(new_socket); + return -1; + } ++ ++ ctx_mt->lst_socket = new_socket; + + return new_socket; + } +@@ -495,6 +509,7 @@ int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection) + const char *service; + int new_socket; + modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; ++ modbus_tcp_mt_t *ctx_mt = ctx->mt_data; + + if (ctx_tcp_pi->node[0] == 0) + node = NULL; /* == any */ +@@ -577,6 +592,8 @@ int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection) + if (new_socket < 0) { + return -1; + } ++ ++ ctx_mt->lst_socket = new_socket; + + return new_socket; + } +@@ -671,6 +688,7 @@ const modbus_backend_t _modbus_tcp_backend = { + _modbus_tcp_pre_check_confirmation, + _modbus_tcp_connect, + _modbus_tcp_close, ++ _modbus_tcp_close_mt, + _modbus_tcp_flush, + _modbus_tcp_select + }; +@@ -693,6 +711,7 @@ const modbus_backend_t _modbus_tcp_pi_backend = { + _modbus_tcp_pre_check_confirmation, + _modbus_tcp_pi_connect, + _modbus_tcp_close, ++ _modbus_tcp_close_mt, + _modbus_tcp_flush, + _modbus_tcp_select + }; +@@ -701,6 +720,7 @@ modbus_t* modbus_new_tcp(const char *ip, int port) + { + modbus_t *ctx; + modbus_tcp_t *ctx_tcp; ++ modbus_tcp_mt_t *ctx_mt; + size_t dest_size; + size_t ret_size; + +@@ -727,6 +747,9 @@ modbus_t* modbus_new_tcp(const char *ip, int port) + + ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); + ctx_tcp = (modbus_tcp_t *)ctx->backend_data; ++ ++ ctx->mt_data = (modbus_tcp_mt_t *) malloc(sizeof(modbus_tcp_mt_t)); ++ ctx_mt = (modbus_tcp_mt_t *)ctx->mt_data; + + dest_size = sizeof(char) * 16; + ret_size = strlcpy(ctx_tcp->ip, ip, dest_size); +@@ -746,6 +769,8 @@ modbus_t* modbus_new_tcp(const char *ip, int port) + + ctx_tcp->port = port; + ctx_tcp->t_id = 0; ++ ++ ctx_mt->lst_socket = -1; + + return ctx; + } +@@ -755,6 +780,7 @@ modbus_t* modbus_new_tcp_pi(const char *node, const char *service) + { + modbus_t *ctx; + modbus_tcp_pi_t *ctx_tcp_pi; ++ modbus_tcp_mt_t *ctx_mt; + size_t dest_size; + size_t ret_size; + +@@ -768,6 +794,9 @@ modbus_t* modbus_new_tcp_pi(const char *node, const char *service) + + ctx->backend_data = (modbus_tcp_pi_t *) malloc(sizeof(modbus_tcp_pi_t)); + ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data; ++ ++ ctx->mt_data = (modbus_tcp_mt_t *) malloc(sizeof(modbus_tcp_mt_t)); ++ ctx_mt = (modbus_tcp_mt_t *)ctx->mt_data; + + dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH; + ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size); +@@ -802,6 +831,8 @@ modbus_t* modbus_new_tcp_pi(const char *node, const char *service) + } + + ctx_tcp_pi->t_id = 0; ++ ++ ctx_mt->lst_socket = -1; + + return ctx; + } +diff --git src/modbus.c src/modbus.c +index b555913..e08f7ae 100644 +--- src/modbus.c ++++ src/modbus.c +@@ -1457,12 +1457,21 @@ void modbus_close(modbus_t *ctx) + ctx->backend->close(ctx); + } + ++void modbus_close_mt(modbus_t *ctx) ++{ ++ if (ctx == NULL) ++ return; ++ ++ ctx->backend->close_mt(ctx); ++} ++ + void modbus_free(modbus_t *ctx) + { + if (ctx == NULL) + return; + + free(ctx->backend_data); ++ free(ctx->mt_data); + free(ctx); + } + +diff --git src/modbus.h src/modbus.h +index 399c41b..e31faf4 100644 +--- src/modbus.h ++++ src/modbus.h +@@ -169,6 +169,7 @@ EXPORT int modbus_get_header_length(modbus_t *ctx); + + EXPORT int modbus_connect(modbus_t *ctx); + EXPORT void modbus_close(modbus_t *ctx); ++EXPORT void modbus_close_mt(modbus_t *ctx); + + EXPORT void modbus_free(modbus_t *ctx); +