diff --git a/builds/msvc/vs2015_xp/libzmq.vcxproj b/builds/msvc/vs2015_xp/libzmq.vcxproj
new file mode 100644
index 00000000..36ee6431
--- /dev/null
+++ b/builds/msvc/vs2015_xp/libzmq.vcxproj
@@ -0,0 +1,255 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {217AD6A0-8CEA-428D-908F-C34B23CECAAF}
+ Win32Proj
+ libzmq
+ 8.1
+
+
+
+ DynamicLibrary
+ true
+ v140_xp
+ NotSet
+
+
+ DynamicLibrary
+ false
+ v140_xp
+ true
+ NotSet
+
+
+ DynamicLibrary
+ true
+ v140
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(ProjectDir)..\..\..\bin\$(PlatformName)\$(Configuration)\$(PlatformToolset)\$(DefaultLinkage)\
+
+
+ true
+
+
+ false
+ $(ProjectDir)..\..\..\bin\$(PlatformName)\$(Configuration)\$(PlatformToolset)\$(DefaultLinkage)\
+
+
+ false
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;DLL_EXPORT;%(PreprocessorDefinitions)
+ $(SolutionDir);%(AdditionalIncludeDirectories)
+
+
+ Windows
+ true
+
+
+
+
+
+
+ Level3
+ Disabled
+ _DEBUG;_WINDOWS;_USRDLL;LIBZMQ_EXPORTS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;DLL_EXPORT;%(PreprocessorDefinitions)
+ MultiThreaded
+ $(SolutionDir)
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ NDEBUG;_WINDOWS;_USRDLL;LIBZMQ_EXPORTS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/builds/msvc/vs2015_xp/platform.hpp b/builds/msvc/vs2015_xp/platform.hpp
new file mode 100644
index 00000000..1694bc12
--- /dev/null
+++ b/builds/msvc/vs2015_xp/platform.hpp
@@ -0,0 +1,15 @@
+#ifndef __PLATFORM_HPP_INCLUDED__
+#define __PLATFORM_HPP_INCLUDED__
+
+#define ZMQ_HAVE_WINDOWS
+#define ZMQ_HAVE_WINDOWS_TARGET_XP
+
+#define ZMQ_BUILD_DRAFT_API
+
+#define ZMQ_USE_SELECT
+#define FD_SETSIZE 1024
+
+#pragma comment(lib,"ws2_32.lib")
+#pragma comment(lib,"Iphlpapi.lib")
+
+#endif
diff --git a/builds/msvc/vs2015_xp/test_zmq/test_multithread.cpp b/builds/msvc/vs2015_xp/test_zmq/test_multithread.cpp
new file mode 100644
index 00000000..30971040
--- /dev/null
+++ b/builds/msvc/vs2015_xp/test_zmq/test_multithread.cpp
@@ -0,0 +1,229 @@
+/*
+Server thread listen ZMQ_SERVER socket and transfer incoming message
+to worker threads by ZMQ_PUSH-ZMQ_PULL
+Worker thread receive message and send back to ZMQ_SERVER socket
+
+Each client thread open CLIENT_CONNECTION ZMQ_CLIENT sockets,
+send random size message to each socket and check server answer
+*/
+
+#define ZMQ_BUILD_DRAFT_API
+#include "../../../../include/zmq.h"
+
+#pragma comment(lib,"libzmq.lib")
+
+#include
+#include
+#include
+#include
+
+#define SERVER_ADDR "tcp://127.0.0.1:12345"
+#define SERVER_WORKER_COUNT 3 // worker threads count
+
+#define CLIENT_COUNT 5 // client threads count
+#define CLIENT_CONNECTION 100 // ZMQ_CLIENT sockets at each client
+#define CLIENT_RECCONECT 1000 // reconnect one socket after messages
+
+#define MESSAGE_MAX_SIZE 1024
+
+//*******************************************************************
+//****** MESSAGE ****************************************************
+//*******************************************************************
+
+void message_fill(zmq_msg_t* msg, int val) {
+ assert(val > 0);
+ int size = sizeof(int) * 2 + val;
+ int rc = zmq_msg_init_size(msg, size); assert(rc == 0);
+ uint8_t* data = (uint8_t*)zmq_msg_data(msg);
+ memcpy(data, &val, sizeof(int));
+ data += sizeof(int);
+ memset(data, val & 0xFF, val);
+ int check_sum = val + (val & 0xFF) * val;
+ data += val;
+ memcpy(data, &check_sum, sizeof(int));
+}
+
+int message_check(zmq_msg_t* msg) {
+ uint8_t* data = (uint8_t*)zmq_msg_data(msg);
+ int size = zmq_msg_size(msg);
+ assert(size > sizeof(int) * 2);
+ // check size
+ int val;
+ memcpy(&val, data, sizeof(int));
+ if(size != sizeof(int) * 2 + val) {
+ fprintf(stderr, "wrong message: val = %d size = %d\n", val, size);
+ return -1;
+ }
+ // check sum
+ data += sizeof(int);
+ int cs = val;
+ for(int i = 0; i < val; i++) {
+ cs += data[i];
+ }
+ data += val;
+ int check_sum;
+ memcpy(&check_sum, data, sizeof(int));
+ if(check_sum != cs) {
+ fprintf(stderr, "wrong message: cs = %d check_sum = %d\n", cs, check_sum);
+ return -1;
+ }
+ return val;
+}
+
+//*******************************************************************
+//****** SERVER *****************************************************
+//*******************************************************************
+
+void *server_ctx = NULL;
+void *server_sock = NULL;
+
+std::atomic worker_cnt[SERVER_WORKER_COUNT] = {0}; // statistic
+
+// worker thread
+void worker(int num) {
+ printf("worker %d start\n", num);
+ void* queue = zmq_socket(server_ctx, ZMQ_PULL); assert(queue);
+ int rc = zmq_connect(queue, "inproc://queue"); assert(rc == 0);
+
+ while (1) {
+ // receive messages from the queue
+ zmq_msg_t msg;
+ rc = zmq_msg_init(&msg); assert(rc == 0);
+ rc = zmq_msg_recv(&msg, queue, 0); assert(rc > 0);
+ // check message
+ //printf("worker %d recv %d bytes at %X from %X\n", num, zmq_msg_size(&msg), zmq_msg_data(&msg), zmq_msg_routing_id(&msg));
+ // send to client
+ rc = zmq_msg_send(&msg, server_sock, 0); assert(rc != -1);
+ worker_cnt[num]++;
+ }
+ zmq_close(queue);
+}
+
+// server thread
+void server() {
+ server_ctx = zmq_ctx_new(); assert(server_ctx);
+ // create queue
+ void* queue = zmq_socket(server_ctx, ZMQ_PUSH); assert(queue);
+ int rc = zmq_bind(queue, "inproc://queue"); assert(rc == 0);
+ // start workers
+ std::thread w[SERVER_WORKER_COUNT];
+ for (int i = 0; i < SERVER_WORKER_COUNT; i++) w[i] = std::thread(worker, i);
+ // ZMQ_SERVER for client messages
+ server_sock = zmq_socket(server_ctx, ZMQ_SERVER); assert(server_sock);
+ rc = zmq_bind(server_sock, SERVER_ADDR); assert(rc == 0);
+
+ while (1) {
+ // wait client message
+ zmq_msg_t msg;
+ rc = zmq_msg_init(&msg); assert(rc == 0);
+ rc = zmq_msg_recv(&msg, server_sock, 0); assert(rc > 0);
+ //printf("recv %d bytes at %X from %X\n", zmq_msg_size(&msg), zmq_msg_data(&msg), zmq_msg_routing_id(&msg));
+ // send message to queue
+ rc = zmq_msg_send(&msg, queue, 0); assert(rc > 0);
+ }
+}
+
+//*******************************************************************
+//****** CLIENT *****************************************************
+//*******************************************************************
+
+std::atomic client_cnt[CLIENT_COUNT] = { 0 }; // statistic
+std::atomic client_ready = 0;
+
+// client thread
+void client(int num)
+{
+ //printf("client %d start. Open %d connections\n", num, CLIENT_CONNECTION);
+
+ void *ctx = zmq_ctx_new(); assert(ctx);
+
+ void *sock[CLIENT_CONNECTION];
+ int rc;
+ // open ZMQ_CLIENT connections
+ for (int i = 0; i < CLIENT_CONNECTION; i++) {
+ sock[i] = zmq_socket(ctx, ZMQ_CLIENT); assert(sock[i]);
+ rc = zmq_connect(sock[i], SERVER_ADDR); assert(rc == 0);
+ // test connection
+ zmq_msg_t msg;
+ int v = rand() % 256 + 1;
+ message_fill(&msg, v);
+ rc = zmq_msg_send(&msg, sock[i], 0); assert(rc > 0);
+ rc = zmq_msg_init(&msg); assert(rc == 0);
+ rc = zmq_msg_recv(&msg, sock[i], 0); assert(rc > 0);
+ rc = message_check(&msg); assert(rc == v);
+ zmq_msg_close(&msg);
+ }
+ printf("client %d open %d connections\n", num, CLIENT_CONNECTION);
+ client_ready++;
+ while (client_ready < CLIENT_COUNT) Sleep(10); // wait while all clients open sockets
+
+ int recconect = 0;
+ while(1) {
+ int val[CLIENT_CONNECTION];
+ zmq_msg_t msg;
+ // send messages
+ for(int i = 0; i < CLIENT_CONNECTION; i++) {
+ val[i] = rand() % MESSAGE_MAX_SIZE + 1;
+ message_fill(&msg, val[i]);
+ rc = zmq_msg_send(&msg, sock[i], 0); assert(rc > 0);
+ }
+ // recv and check
+ for (int i = 0; i < CLIENT_CONNECTION; i++) {
+ rc = zmq_msg_init(&msg); assert(rc == 0);
+ rc = zmq_msg_recv(&msg, sock[i], 0); assert(rc > 0);
+ rc = message_check(&msg);
+ if(rc != val[i] && rc > 0) {
+ fprintf(stderr, "wrong message: send %d recv %d \n", val[i], rc);
+ }
+ zmq_msg_close(&msg);
+ client_cnt[num]++;
+ }
+ // reconnect one
+ recconect++;
+ if(recconect == CLIENT_RECCONECT) {
+ int n = rand() % CLIENT_CONNECTION;
+ zmq_close(sock[n]);
+ sock[n] = zmq_socket(ctx, ZMQ_CLIENT); assert(sock[n]);
+ int rc = zmq_connect(sock[n], SERVER_ADDR); assert(rc == 0);
+ }
+ }
+}
+
+//*******************************************************************
+int main (void) {
+ int v1, v2, v3; zmq_version(&v1, &v2, &v3);
+ printf("ZMQ version %d.%d.%d. Compile %s %s\n", v1, v2, v3, __DATE__, __TIME__);
+
+ std::thread ct[CLIENT_COUNT];
+ for (int i = 0; i < CLIENT_COUNT; i++) ct[i] = std::thread(client, i);
+
+ std::thread st(server);
+
+ int w[SERVER_WORKER_COUNT] = { 0 };
+ int c[CLIENT_COUNT] = { 0 };
+ int total = 0;
+
+ while(1) {
+ Sleep(1000);
+ if (client_ready < CLIENT_COUNT) continue;
+ // check workers
+ for(int i = 0; i < SERVER_WORKER_COUNT; i++) {
+ if(w[i] == worker_cnt[i]) {
+ fprintf(stderr, "worker %d not work \n", i);
+ }
+ w[i] = worker_cnt[i];
+ }
+ // check clients
+ int t = 0;
+ for (int i = 0; i < CLIENT_COUNT; i++) {
+ if (c[i] == client_cnt[i]) {
+ fprintf(stderr, "client %d not work \n", i);
+ }
+ c[i] = client_cnt[i];
+ t += c[i];
+ }
+ printf("\rTotal %d messages. Speed %d per second ", t, t - total);
+ total = t;
+ }
+ return 0;
+}
diff --git a/builds/msvc/vs2015_xp/test_zmq/test_zmq.vcxproj b/builds/msvc/vs2015_xp/test_zmq/test_zmq.vcxproj
new file mode 100644
index 00000000..32a45997
--- /dev/null
+++ b/builds/msvc/vs2015_xp/test_zmq/test_zmq.vcxproj
@@ -0,0 +1,155 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {101324AB-CF3E-4D99-8B69-2000CEA487B6}
+ Win32Proj
+ test_zmq
+ 8.1
+
+
+
+ Application
+ true
+ v140_xp
+ NotSet
+
+
+ Application
+ false
+ v140_xp
+ true
+ NotSet
+
+
+ Application
+ true
+ v140
+ Unicode
+
+
+ Application
+ false
+ v140
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(ProjectDir)..\..\..\..\bin\$(PlatformName)\$(Configuration)\$(PlatformToolset)\$(DefaultLinkage)\
+
+
+ true
+
+
+ false
+ $(ProjectDir)..\..\..\..\bin\$(PlatformName)\$(Configuration)\$(PlatformToolset)\$(DefaultLinkage)\
+
+
+ false
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+ $(ProjectDir)..\..\..\..\bin\$(PlatformName)\$(Configuration)\$(PlatformToolset)\$(DefaultLinkage)\
+
+
+
+
+
+
+ Level3
+ Disabled
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ MultiThreaded
+
+
+ Console
+ true
+ true
+ true
+ $(ProjectDir)..\..\..\..\bin\$(PlatformName)\$(Configuration)\$(PlatformToolset)\$(DefaultLinkage)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/condition_variable.hpp b/src/condition_variable.hpp
index 11f4a0f4..d8c80953 100644
--- a/src/condition_variable.hpp
+++ b/src/condition_variable.hpp
@@ -81,9 +81,15 @@ namespace zmq
#else
+#ifdef ZMQ_HAVE_WINDOWS_TARGET_XP
+#include
+#include
+#endif
+
namespace zmq
{
+#ifndef ZMQ_HAVE_WINDOWS_TARGET_XP
class condition_variable_t
{
public:
@@ -126,7 +132,54 @@ namespace zmq
condition_variable_t (const condition_variable_t&);
void operator = (const condition_variable_t&);
};
+#else
+ class condition_variable_t
+ {
+ public:
+ inline condition_variable_t()
+ {
+
+ }
+
+ inline ~condition_variable_t()
+ {
+
+ }
+
+ inline int wait(mutex_t* mutex_, int timeout_)
+ {
+ std::unique_lock lck(mtx); // lock mtx
+ mutex_->unlock(); // unlock mutex_
+ int res = 0;
+ if(timeout_ == -1) {
+ cv.wait(lck); // unlock mtx and wait cv.notify_all(), lock mtx after cv.notify_all()
+ } else if (cv.wait_for(lck, std::chrono::milliseconds(timeout_)) == std::cv_status::timeout) {
+ // time expired
+ errno = EAGAIN;
+ res = -1;
+ }
+ lck.unlock(); // unlock mtx
+ mutex_->lock(); // lock mutex_
+ return res;
+ }
+
+ inline void broadcast()
+ {
+ std::unique_lock lck(mtx); // lock mtx
+ cv.notify_all();
+ }
+
+ private:
+
+ std::condition_variable cv;
+ std::mutex mtx;
+
+ // Disable copy construction and assignment.
+ condition_variable_t(const condition_variable_t&);
+ void operator = (const condition_variable_t&);
+ };
+#endif
}
#endif
diff --git a/src/tcp_address.cpp b/src/tcp_address.cpp
index 4987e3ee..d0aee8c1 100644
--- a/src/tcp_address.cpp
+++ b/src/tcp_address.cpp
@@ -245,7 +245,9 @@ int zmq::tcp_address_t::get_interface_name(unsigned long index, char ** dest) co
char * if_name_result = NULL;
+#ifndef ZMQ_HAVE_WINDOWS_TARGET_XP
if_name_result = if_indextoname(index, buffer);
+#endif
if (if_name_result == NULL) {
free(buffer);