From 3cad2a32a4dfe45bc5f76b985e86b576fb199df9 Mon Sep 17 00:00:00 2001 From: Marek Sedlak Date: Tue, 3 Mar 2026 00:19:42 +0100 Subject: [PATCH] Fix GitHub Actions CI and Windows/MinGW build compatibility Update GitHub Actions workflow: - Upgrade deprecated actions (checkout, upload/download-artifact, codecov, gh-release) to v4/v2 - Replace deprecated ::set-output with $GITHUB_OUTPUT - Update runner images: remove deprecated macos-13, add macos-15, update Ubuntu to 22.04/24.04 and Windows to 2022/2025 - Make Windows coverage generation non-fatal - Clean up stale gcov artifacts before coverage runs - Disable fail-fast on test matrices Fix Windows/MinGW build errors: - Modernize K&R-style C function definitions to ANSI C in win32/getopt.c, getopt1.c, and getopt.h - Rename reserved __argc/__argv parameter names in getopt.h - Fix implicit declarations: GetAdaptersInfo (add iphlpapi.h), getenv (add stdlib.h), _byteswap_ulong (add stdlib.h) - Fix type mismatches: setsockopt (const char* cast), CryptGenRandom (BYTE* cast), pthread_create/mutex_trylock macros - Extend N2N_THREAD_RETURN/PARAMETER_DATATYPE to cover __MINGW32__ - Exclude MinGW from system getopt.h to use local version Fix Python and C linting: - Fix flake8 E275 in n2n-ctl and n2n-httpd (assert syntax) - Fix uncrustify indentation in HASH_FIND_PEER/HASH_ADD_PEER macros Made-with: Cursor --- .github/workflows/cmake-linux.yml | 2 +- .github/workflows/tests.yml | 101 +++++++++++++++--------------- include/n2n.h | 4 +- include/n2n_define.h | 6 +- include/portable_endian.h | 4 ++ scripts/n2n-ctl | 4 +- scripts/n2n-httpd | 4 +- src/edge_utils.c | 8 +++ src/random_numbers.c | 2 +- src/sn_utils.c | 8 +++ win32/getopt.c | 45 ++++--------- win32/getopt.h | 32 ++++------ win32/getopt1.c | 20 ++---- win32/n2n_win32.h | 10 +-- 14 files changed, 115 insertions(+), 135 deletions(-) diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index a018a58d4..a5fe979fa 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -24,7 +24,7 @@ jobs: - os: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Create Build Environment # Some projects don't allow in-source building, so create a separate diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3604462ba..37c97448c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run minimal test set run: | ./autogen.sh @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Make the makefiles run: | @@ -44,14 +44,14 @@ jobs: name: Test Linux runs-on: ${{ matrix.os }} strategy: - fail-fast: true + fail-fast: false matrix: os: - - ubuntu-20.04 - ubuntu-22.04 + - ubuntu-24.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install essential run: | @@ -85,39 +85,40 @@ jobs: - if: ${{ always() }} name: Upload tests output - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: tests-out + name: tests-out-${{ matrix.os }} path: tests - name: Generate coverage reports run: | + rm -f a-conftest.gcno a-conftest.gcda make gcov make cover COVERAGEDIR=coverage/${{ matrix.os }} shell: bash - name: Upload gcovr report artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: coverage + name: coverage-${{ matrix.os }} path: coverage - name: Upload data to codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 test_macos: needs: smoketest name: Test MacOS runs-on: ${{ matrix.os }} strategy: - fail-fast: true + fail-fast: false matrix: os: - - macos-11 - - macos-12 + - macos-14 + - macos-15 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install packages run: | @@ -150,9 +151,9 @@ jobs: - if: ${{ always() }} name: Upload tests output - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: tests-out + name: tests-out-${{ matrix.os }} path: tests - name: Generate coverage reports @@ -169,27 +170,27 @@ jobs: shell: bash # - name: Upload gcovr report artifact - # uses: actions/upload-artifact@v2 + # uses: actions/upload-artifact@v4 # with: - # name: coverage + # name: coverage-${{ matrix.os }} # path: coverage - name: Upload data to codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 test_windows: needs: smoketest name: Test Windows runs-on: ${{ matrix.os }} strategy: - fail-fast: true + fail-fast: false matrix: os: - - windows-2019 - windows-2022 + - windows-2025 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: generate a makefile and use it to install more packages run: | @@ -218,18 +219,19 @@ jobs: - if: ${{ always() }} name: Upload tests output - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: tests-out + name: tests-out-${{ matrix.os }} path: tests - name: Generate coverage data run: | - make gcov + make gcov || true shell: bash - name: Upload data to codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 + continue-on-error: true package_dpkg: name: Package for Debian/Ubuntu @@ -246,7 +248,7 @@ jobs: - i386 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -280,9 +282,9 @@ jobs: make - name: Upload dpkg - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: packages-dpkg + name: packages-dpkg-${{ matrix.arch }} path: packages/debian/*.deb package_rpm: @@ -292,7 +294,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -321,7 +323,7 @@ jobs: mv ../rpmbuild ./ - name: Upload rpm - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: packages-rpm path: rpmbuild/RPMS/x86_64/*.rpm @@ -333,7 +335,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Fix Checkout run: | @@ -351,9 +353,9 @@ jobs: make install DESTDIR=binaries/x86_64-pc-mingw64 - name: Upload binary artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: binaries + name: binaries-windows path: binaries binaries_macos: @@ -369,7 +371,7 @@ jobs: - arm64-apple-macos steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Fix Checkout run: | @@ -393,7 +395,7 @@ jobs: - if: ${{ failure() }} name: Upload config.log output - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: config-log-${{ matrix.arch }} path: config.log @@ -404,9 +406,9 @@ jobs: make install DESTDIR=binaries/${{ matrix.arch }} - name: Upload binary artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: binaries + name: binaries-macos-${{ matrix.arch }} path: binaries binaries_macos_universal: @@ -416,7 +418,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Fix Checkout run: | @@ -444,9 +446,9 @@ jobs: make install DESTDIR=binaries/universal-apple-darwin - name: Upload binary artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: binaries + name: binaries-macos-universal path: binaries binaries_linux_crosscompile: @@ -469,7 +471,7 @@ jobs: - mipsel-linux-gnu steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Fix Checkout run: | @@ -497,9 +499,9 @@ jobs: make install DESTDIR=binaries/${{ matrix.arch }} - name: Upload binary artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: binaries + name: binaries-linux-${{ matrix.arch }} path: binaries # Given the clearly documented use of annotated tags to signal releases, @@ -526,7 +528,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Fix Checkout run: | @@ -536,22 +538,23 @@ jobs: id: get_tagtype run: | TYPE=$(git cat-file -t $GITHUB_REF) - echo "::set-output name=TAGTYPE::$TYPE" + echo "TAGTYPE=$TYPE" >> "$GITHUB_OUTPUT" echo ========== echo REF=$GITHUB_REF echo TAGTYPE=$TYPE - name: Fetch all Artifacts if: steps.get_tagtype.outputs.TAGTYPE == 'tag' - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: path: artifacts + merge-multiple: true - name: Upload Assets to Release if: steps.get_tagtype.outputs.TAGTYPE == 'tag' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: prerelease: true files: | - artifacts/packages-dpkg/*.deb + artifacts/packages-dpkg*/*.deb artifacts/packages-rpm/*.rpm diff --git a/include/n2n.h b/include/n2n.h index c733cce31..794031923 100644 --- a/include/n2n.h +++ b/include/n2n.h @@ -65,9 +65,9 @@ #include #endif -#ifndef _MSC_VER +#if !defined(_MSC_VER) && !defined(__MINGW32__) #include -#endif /* #ifndef _MSC_VER */ +#endif #include #include diff --git a/include/n2n_define.h b/include/n2n_define.h index 9b0769bdc..8b6d509c6 100644 --- a/include/n2n_define.h +++ b/include/n2n_define.h @@ -112,9 +112,9 @@ enum sn_purge {SN_PURGEABLE = 0, SN_UNPURGEABLE = 1}; #define DEFAULT_MTU 1290 #define HASH_ADD_PEER(head,add) \ - HASH_ADD(hh,head,mac_addr,sizeof(n2n_mac_t),add) + HASH_ADD(hh,head,mac_addr,sizeof(n2n_mac_t),add) #define HASH_FIND_PEER(head,mac,out) \ - HASH_FIND(hh,head,mac,sizeof(n2n_mac_t),out) + HASH_FIND(hh,head,mac,sizeof(n2n_mac_t),out) #define N2N_EDGE_SN_HOST_SIZE 48 #define N2N_EDGE_SUP_ATTEMPTS 3 /* Number of failed attmpts before moving on to next supernode. */ #define N2N_PATHNAME_MAXLEN 256 @@ -196,7 +196,7 @@ enum skip_add {SN_ADD = 0, SN_ADD_SKIP = 1, SN_ADD_ADDED = 2}; #define N2N_IFNAMSIZ 16 /* 15 chars * NULL */ #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(__MINGW32__) #define N2N_THREAD_RETURN_DATATYPE DWORD WINAPI #define N2N_THREAD_PARAMETER_DATATYPE LPVOID #else diff --git a/include/portable_endian.h b/include/portable_endian.h index 12a30b979..539013efb 100644 --- a/include/portable_endian.h +++ b/include/portable_endian.h @@ -17,6 +17,10 @@ #endif +#if defined(__WINDOWS__) +# include +#endif + #if defined(__linux__) || defined(__CYGWIN__) /* Define necessary macros for the header to expose all fields. */ # if !defined(_BSD_SOURCE) diff --git a/scripts/n2n-ctl b/scripts/n2n-ctl index ab59f3620..7cd7f8e84 100755 --- a/scripts/n2n-ctl +++ b/scripts/n2n-ctl @@ -47,12 +47,12 @@ class JsonUDP(): # TODO: We assume the first packet we get will be tagged for us # and be either an "error" or a "begin" - assert(data['_tag'] == tagstr) + assert data['_tag'] == tagstr if data['_type'] == 'error': raise ValueError('Error: {}'.format(data['error'])) - assert(data['_type'] == 'begin') + assert data['_type'] == 'begin' # Ideally, we would confirm that this is our "begin", but that # would need the cmd passed into this method, and that would diff --git a/scripts/n2n-httpd b/scripts/n2n-httpd index 9211915aa..3f6bc90b8 100755 --- a/scripts/n2n-httpd +++ b/scripts/n2n-httpd @@ -62,12 +62,12 @@ class JsonUDP(): # TODO: We assume the first packet we get will be tagged for us # and be either an "error" or a "begin" - assert(data['_tag'] == tagstr) + assert data['_tag'] == tagstr if data['_type'] == 'error': raise ValueError('Error: {}'.format(data['error'])) - assert(data['_type'] == 'begin') + assert data['_type'] == 'begin' # Ideally, we would confirm that this is our "begin", but that # would need the cmd passed into this method, and that would diff --git a/src/edge_utils.c b/src/edge_utils.c index ec1421509..a7ab4da12 100644 --- a/src/edge_utils.c +++ b/src/edge_utils.c @@ -1074,7 +1074,11 @@ static ssize_t sendto_sock (n2n_edge_t *eee, const void * buf, // if the connection is tcp, i.e. not the regular sock... if(eee->conf.connect_tcp) { +#ifdef WIN32 + setsockopt(eee->sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&value, sizeof(value)); +#else setsockopt(eee->sock, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); +#endif value = 1; #ifdef LINUX setsockopt(eee->sock, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)); @@ -1093,7 +1097,11 @@ static ssize_t sendto_sock (n2n_edge_t *eee, const void * buf, // if the connection is tcp, i.e. not the regular sock... if(eee->conf.connect_tcp) { value = 1; /* value should still be set to 1 */ +#ifdef WIN32 + setsockopt(eee->sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&value, sizeof(value)); +#else setsockopt(eee->sock, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); +#endif #ifdef LINUX value = 0; setsockopt(eee->sock, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)); diff --git a/src/random_numbers.c b/src/random_numbers.c index 0adb8c6f5..6ebcc7fea 100644 --- a/src/random_numbers.c +++ b/src/random_numbers.c @@ -161,7 +161,7 @@ uint64_t n2n_seed (void) { HCRYPTPROV crypto_provider; CryptAcquireContext (&crypto_provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); - CryptGenRandom (crypto_provider, 8, &seed); + CryptGenRandom (crypto_provider, 8, (BYTE *)&seed); CryptReleaseContext (crypto_provider, 0); ret += seed; #endif diff --git a/src/sn_utils.c b/src/sn_utils.c index c7371dd5a..96623a669 100644 --- a/src/sn_utils.c +++ b/src/sn_utils.c @@ -517,7 +517,11 @@ static ssize_t sendto_sock(n2n_sn_t *sss, // if the connection is tcp, i.e. not the regular sock... if((socket_fd >= 0) && (socket_fd != sss->sock)) { +#ifdef WIN32 + setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&value, sizeof(value)); +#else setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); +#endif value = 1; #ifdef LINUX setsockopt(socket_fd, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)); @@ -537,7 +541,11 @@ static ssize_t sendto_sock(n2n_sn_t *sss, // if the connection is tcp, i.e. not the regular sock... if((socket_fd >= 0) && (socket_fd != sss->sock)) { value = 1; /* value should still be set to 1 */ +#ifdef WIN32 + setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&value, sizeof(value)); +#else setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); +#endif #ifdef LINUX value = 0; setsockopt(socket_fd, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)); diff --git a/win32/getopt.c b/win32/getopt.c index c938ea326..621f8b125 100644 --- a/win32/getopt.c +++ b/win32/getopt.c @@ -39,9 +39,12 @@ # endif #endif +#include "getopt.h" + #include #ifdef WIN32 #include +#include #endif /* Comment out all this code if we are using the GNU C Library, and are not @@ -104,8 +107,6 @@ GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ -#include "getopt.h" - /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. @@ -221,17 +222,10 @@ static char *posixly_correct; # endif #endif -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -#ifndef getenv -extern char *getenv (); -#endif +/* getenv is declared in stdlib.h which we include for WIN32 */ static char * -my_index (str, chr) - const char *str; - int chr; +my_index (const char *str, int chr) { while (*str) { @@ -319,8 +313,7 @@ static void exchange (char **); #endif static void -exchange (argv) - char **argv; +exchange (char **argv) { int bottom = first_nonopt; int middle = last_nonopt; @@ -400,14 +393,8 @@ exchange (argv) /* Initialize the internal data when the first call is made. */ -#if defined __STDC__ && __STDC__ -static const char *_getopt_initialize (int, char *const *, const char *); -#endif static const char * -_getopt_initialize (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; +_getopt_initialize (int argc, char *const *argv, const char *optstring) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped @@ -526,13 +513,8 @@ _getopt_initialize (argc, argv, optstring) long-named options. */ int -_getopt_internal (argc, argv, optstring, longopts, longind, long_only) - int argc; - char *const *argv; - const char *optstring; - const struct option *longopts; - int *longind; - int long_only; +_getopt_internal (int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longind, int long_only) { int print_errors = opterr; if (optstring[0] == ':') @@ -987,10 +969,7 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only) } int -getopt____ (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; +getopt____ (int argc, char *const *argv, const char *optstring) { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, @@ -1006,9 +985,7 @@ getopt____ (argc, argv, optstring) the above definition of `getopt'. */ int -main (argc, argv) - int argc; - char **argv; +main (int argc, char **argv) { int c; int digit_optind = 0; diff --git a/win32/getopt.h b/win32/getopt.h index 91eb54e55..27bab50ad 100644 --- a/win32/getopt.h +++ b/win32/getopt.h @@ -126,38 +126,28 @@ struct option arguments to the option '\0'. This behavior is specific to the GNU `getopt'. */ -#if defined __STDC__ && __STDC__ # ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ -extern int getopt (int __argc, char *const *__argv, const char *__shortopts); +extern int getopt (int argc, char *const *argv, const char *shortopts); # else /* not __GNU_LIBRARY__ */ -extern int getopt (); +extern int getopt (int argc, char *const *argv, const char *shortopts); # endif /* __GNU_LIBRARY__ */ # ifndef __need_getopt -extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, - const struct option *__longopts, int *__longind); -extern int getopt_long_only (int __argc, char *const *__argv, - const char *__shortopts, - const struct option *__longopts, int *__longind); +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ -extern int _getopt_internal (int __argc, char *const *__argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - int __long_only); +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); # endif -#else /* not __STDC__ */ -extern int getopt (); -# ifndef __need_getopt -extern int getopt_long (); -extern int getopt_long_only (); - -extern int _getopt_internal (); -# endif -#endif /* __STDC__ */ #ifdef __cplusplus } diff --git a/win32/getopt1.c b/win32/getopt1.c index f7ca91c26..9295e45c4 100644 --- a/win32/getopt1.c +++ b/win32/getopt1.c @@ -64,12 +64,8 @@ #endif int -getopt_long (argc, argv, options, long_options, opt_index) - int argc; - char *const *argv; - const char *options; - const struct option *long_options; - int *opt_index; +getopt_long (int argc, char *const *argv, const char *options, + const struct option *long_options, int *opt_index) { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } @@ -80,12 +76,8 @@ getopt_long (argc, argv, options, long_options, opt_index) instead. */ int -getopt_long_only (argc, argv, options, long_options, opt_index) - int argc; - char *const *argv; - const char *options; - const struct option *long_options; - int *opt_index; +getopt_long_only (int argc, char *const *argv, const char *options, + const struct option *long_options, int *opt_index) { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } @@ -98,9 +90,7 @@ getopt_long_only (argc, argv, options, long_options, opt_index) #include int -main (argc, argv) - int argc; - char **argv; +main (int argc, char **argv) { int c; int digit_optind = 0; diff --git a/win32/n2n_win32.h b/win32/n2n_win32.h index 8cddbb0e1..a504d0ff9 100644 --- a/win32/n2n_win32.h +++ b/win32/n2n_win32.h @@ -26,8 +26,8 @@ #include #include #include +#include #if defined(_MSC_VER) -#include #pragma comment(lib,"Iphlpapi.lib") #endif #include @@ -105,9 +105,9 @@ typedef struct tuntap_dev { #define pthread_mutex_t HANDLE #define pthread_create(p_thread_handle, attr, thread_func, p_param) \ - (*p_thread_handle = CreateThread(0 /* default security flags */, 0 /*default stack*/, \ - thread_func, p_param, 0 /* default creation flags */, \ - NULL) == 0) + ((*p_thread_handle = CreateThread(0 /* default security flags */, 0 /*default stack*/, \ + (LPTHREAD_START_ROUTINE)thread_func, p_param, 0 /* default creation flags */, \ + NULL)) == NULL) #define pthread_cancel(p_thread_handle) \ TerminateThread(p_thread_handle, 0) @@ -120,7 +120,7 @@ typedef struct tuntap_dev { WaitForSingleObject(*mutex, INFINITE) #define pthread_mutex_trylock(mutex) \ - WaitForSingleObject(*mutex, NULL) + WaitForSingleObject(*mutex, 0) #define pthread_mutex_unlock(mutex) \ ReleaseMutex(*mutex)