Commit a1a11c74 authored by Dominik Charousset's avatar Dominik Charousset

cleaned up MinGW changes, removed node_id::get

this patch streamlines the recent patch from ajac: enforce consistent
coding style, removed the superfluous windows_tcp class and moved
the node_id::get to the MM, to guarantee WSAStartup has been called
before accessing network devices
parent 5727f72b
...@@ -116,7 +116,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") ...@@ -116,7 +116,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LIBCPPA_PLATFORM_SRC src/middleman_event_handler_poll.cpp) set(LIBCPPA_PLATFORM_SRC src/middleman_event_handler_poll.cpp)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Window") elseif (${CMAKE_SYSTEM_NAME} MATCHES "Window")
set(LIBCPPA_PLATFORM_SRC src/middleman_event_handler_poll.cpp src/backtrace.cpp) set(LIBCPPA_PLATFORM_SRC src/middleman_event_handler_poll.cpp src/execinfo_windows.cpp)
else () else ()
message(FATAL_ERROR "This platform is not supported by libcppa") message(FATAL_ERROR "This platform is not supported by libcppa")
endif() endif()
...@@ -129,8 +129,7 @@ file(GLOB LIBCPPA_HDRS "cppa/*.hpp" ...@@ -129,8 +129,7 @@ file(GLOB LIBCPPA_HDRS "cppa/*.hpp"
"cppa/io/*.hpp" "cppa/io/*.hpp"
"cppa/opencl/*.hpp" "cppa/opencl/*.hpp"
"cppa/qtsupport/*.hpp" "cppa/qtsupport/*.hpp"
"cppa/util/*.hpp" "cppa/util/*.hpp")
"cppa/windows/*.hpp")
# list cpp files including platform-dependent files # list cpp files including platform-dependent files
set(LIBCPPA_SRC set(LIBCPPA_SRC
...@@ -206,7 +205,6 @@ set(LIBCPPA_SRC ...@@ -206,7 +205,6 @@ set(LIBCPPA_SRC
src/serializer.cpp src/serializer.cpp
src/shared_spinlock.cpp src/shared_spinlock.cpp
src/singleton_manager.cpp src/singleton_manager.cpp
src/socketpair.cpp
src/string_serialization.cpp src/string_serialization.cpp
src/sync_request_bouncer.cpp src/sync_request_bouncer.cpp
src/thread_pool_scheduler.cpp src/thread_pool_scheduler.cpp
...@@ -248,13 +246,6 @@ if (ENABLE_OPENCL) ...@@ -248,13 +246,6 @@ if (ENABLE_OPENCL)
add_definitions(-DCPPA_OPENCL) add_definitions(-DCPPA_OPENCL)
endif (ENABLE_OPENCL) endif (ENABLE_OPENCL)
if (MINGW)
set(LIBCPPA_SRC
${LIBCPPA_SRC}
src/windows/windows_tcp.cpp)
endif (MINGW)
if (DISABLE_MEM_MANAGEMENT) if (DISABLE_MEM_MANAGEMENT)
add_definitions(-DCPPA_DISABLE_MEM_MANAGEMENT) add_definitions(-DCPPA_DISABLE_MEM_MANAGEMENT)
endif (DISABLE_MEM_MANAGEMENT) endif (DISABLE_MEM_MANAGEMENT)
......
...@@ -351,3 +351,5 @@ unit_testing/test_yield_interface.cpp ...@@ -351,3 +351,5 @@ unit_testing/test_yield_interface.cpp
cppa/may_have_timeout.hpp cppa/may_have_timeout.hpp
examples/message_passing/typed_calculator.cpp examples/message_passing/typed_calculator.cpp
examples/aout.cpp examples/aout.cpp
cppa/detail/execinfo_windows.hpp
src/execinfo_windows.cpp
This diff is collapsed.
...@@ -11,12 +11,11 @@ ...@@ -11,12 +11,11 @@
* * * *
* Copyright (C) 2011-2013 * * Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> * * Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* Raphael Hiesgen <raphael.hiesgen@haw-hamburg.de> *
* * * *
* This file is part of libcppa. * * This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under * * libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the * * the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation, either version 3 of the License * * Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. * * or (at your option) any later version. *
* * * *
* libcppa is distributed in the hope that it will be useful, * * libcppa is distributed in the hope that it will be useful, *
...@@ -28,44 +27,45 @@ ...@@ -28,44 +27,45 @@
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. * * along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/ \******************************************************************************/
#include <atomic>
#include <iostream>
#include "cppa/logging.hpp" /******************************************************************************\
#include "cppa/exception.hpp" * Based on work by the mingw-w64 project; *
* original header: *
#include "cppa/windows/windows_tcp.hpp" * *
* Copyright (c) 2012 mingw-w64 project *
using namespace std; * *
* Contributing author: Kai Tietz *
namespace cppa { namespace windows { * *
* Permission is hereby granted, free of charge, to any person obtaining a *
void windows_tcp::initialize() * copy of this software and associated documentation files (the "Software"), *
{ * to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
WSADATA WinsockData; * and/or sell copies of the Software, and to permit persons to whom the *
if (WSAStartup(MAKEWORD(2, 2), &WinsockData) != 0) { * Software is furnished to do so, subject to the following conditions: *
ostringstream oss; * *
oss << "libcppa WSAStartup failed. "; * The above copyright notice and this permission notice shall be included in *
CPPA_LOGMF(CPPA_ERROR, oss.str()); * all copies or substantial portions of the Software. *
throw runtime_error(oss.str()); * *
} * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
} * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
* DEALINGS IN THE SOFTWARE. *
\******************************************************************************/
void windows_tcp::destroy() { #ifndef CPPA_DETAIL_EXECINFO_WINDOWS_HPP
WSACleanup(); #define CPPA_DETAIL_EXECINFO_WINDOWS_HPP
delete this;
}
void windows_tcp::dispose() { namespace cppa {
WSACleanup(); namespace detail {
delete this;
}
windows_tcp* get_windows_tcp() { int backtrace(void** buffer, int size);
return detail::singleton_manager::get_windows_tcp(); char** backtrace_symbols(void* const* buffer, int size);
} void backtrace_symbols_fd(void* const* buffer, int size, int fd);
} } // namespace cppa::windows } // namespace detail
} // namespace cppa
#endif // CPPA_DETAIL_EXECINFO_WINDOWS_HPP
...@@ -31,57 +31,24 @@ ...@@ -31,57 +31,24 @@
#ifndef FD_UTIL_HPP #ifndef FD_UTIL_HPP
#define FD_UTIL_HPP #define FD_UTIL_HPP
#include <unistd.h> #include <string>
#include <utility> // std::pair
#include <unistd.h> // ssize_t
#include "cppa/config.hpp" #include "cppa/config.hpp"
namespace cppa { namespace detail { namespace fd_util { namespace cppa { namespace detail { namespace fd_util {
#if defined(CPPA_MACOS) || defined(CPPA_LINUX) std::string last_socket_error_as_string();
// throws ios_base::failure and adds errno failure if @p add_errno_failure
void throw_io_failure(const char* what, bool add_errno_failure = true);
// returns true if fd is nonblocking
// throws @p ios_base::failure on error
bool nonblocking(native_socket_type fd);
// sets fd to nonblocking if <tt>set_nonblocking == true</tt>
// or to blocking if <tt>set_nonblocking == false</tt>
// throws @p ios_base::failure on error
void nonblocking(native_socket_type fd, bool new_value);
// returns true if fd is nodelay socket
// throws @p ios_base::failure on error
bool tcp_nodelay(native_socket_type fd);
// returns true if fd is nodelay socket
// throws @p ios_base::failure on error
void tcp_nodelay(native_socket_type fd, bool new_value);
// reads @p result and @p errno and throws @p ios_base::failure on error
void handle_write_result(ssize_t result, bool is_nonblocking_io);
// reads @p result and @p errno and throws @p ios_base::failure on error
void handle_read_result(ssize_t result, bool is_nonblocking_io);
#else
// throws ios_base::failure and adds errno failure if @p add_errno_failure // throws ios_base::failure and adds errno failure if @p add_errno_failure
void throw_io_failure(const char* what, bool add_errno_failure = true); void throw_io_failure(const char* what, bool add_errno_failure = true);
// returns true if fd is nonblocking
// throws @p ios_base::failure on error
bool nonblocking(native_socket_type fd);
// sets fd to nonblocking if <tt>set_nonblocking == true</tt> // sets fd to nonblocking if <tt>set_nonblocking == true</tt>
// or to blocking if <tt>set_nonblocking == false</tt> // or to blocking if <tt>set_nonblocking == false</tt>
// throws @p ios_base::failure on error // throws @p ios_base::failure on error
void nonblocking(native_socket_type fd, bool new_value); void nonblocking(native_socket_type fd, bool new_value);
// returns true if fd is nodelay socket
// throws @p ios_base::failure on error
bool tcp_nodelay(native_socket_type fd);
// returns true if fd is nodelay socket // returns true if fd is nodelay socket
// throws @p ios_base::failure on error // throws @p ios_base::failure on error
void tcp_nodelay(native_socket_type fd, bool new_value); void tcp_nodelay(native_socket_type fd, bool new_value);
...@@ -92,7 +59,7 @@ void handle_write_result(ssize_t result, bool is_nonblocking_io); ...@@ -92,7 +59,7 @@ void handle_write_result(ssize_t result, bool is_nonblocking_io);
// reads @p result and @p errno and throws @p ios_base::failure on error // reads @p result and @p errno and throws @p ios_base::failure on error
void handle_read_result(ssize_t result, bool is_nonblocking_io); void handle_read_result(ssize_t result, bool is_nonblocking_io);
#endif std::pair<native_socket_type, native_socket_type> create_pipe();
} } } // namespace cppa::detail::fd_util } } } // namespace cppa::detail::fd_util
......
...@@ -44,8 +44,6 @@ namespace cppa { namespace io { class middleman; } } ...@@ -44,8 +44,6 @@ namespace cppa { namespace io { class middleman; } }
namespace cppa { namespace opencl { class opencl_metainfo; } } namespace cppa { namespace opencl { class opencl_metainfo; } }
namespace cppa { namespace windows { class windows_tcp; } }
namespace cppa { namespace detail { namespace cppa { namespace detail {
class empty_tuple; class empty_tuple;
...@@ -82,8 +80,6 @@ class singleton_manager { ...@@ -82,8 +80,6 @@ class singleton_manager {
static opencl::opencl_metainfo* get_opencl_metainfo(); static opencl::opencl_metainfo* get_opencl_metainfo();
static windows::windows_tcp* get_windows_tcp();
private: private:
/* /*
......
/*
Copyright (c) 2012 mingw-w64 project
Contributing author: Kai Tietz
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
int backtrace (void **buffer, int size);
char ** backtrace_symbols (void *const *buffer, int size);
void backtrace_symbols_fd (void *const *buffer, int size, int fd);
...@@ -173,6 +173,11 @@ class middleman { ...@@ -173,6 +173,11 @@ class middleman {
*/ */
inline actor_namespace& get_namespace(); inline actor_namespace& get_namespace();
/**
* @brief Returns the node of this middleman.
*/
inline const node_id_ptr& node() const;
protected: protected:
// creates a middleman instance // creates a middleman instance
...@@ -197,12 +202,14 @@ class middleman { ...@@ -197,12 +202,14 @@ class middleman {
}; };
int dumb_socketpair(native_socket_type socks[2], int make_overlapped);
inline actor_namespace& middleman::get_namespace() { inline actor_namespace& middleman::get_namespace() {
return m_namespace; return m_namespace;
} }
const node_id_ptr& middleman::node() const {
return m_node;
}
} } // namespace cppa::io } } // namespace cppa::io
#endif // MIDDLEMAN_HPP #endif // MIDDLEMAN_HPP
...@@ -34,14 +34,10 @@ ...@@ -34,14 +34,10 @@
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include <cppa/config.hpp>
#ifdef CPPA_WINDOWS
#include <cppa/execinfo_windows.h>
#else
#include <execinfo.h>
#endif
#include <type_traits> #include <type_traits>
#include "cppa/config.hpp"
#include "cppa/singletons.hpp" #include "cppa/singletons.hpp"
#include "cppa/abstract_actor.hpp" #include "cppa/abstract_actor.hpp"
...@@ -171,8 +167,8 @@ oss_wr operator<<(oss_wr&& lhs, T rhs) { ...@@ -171,8 +167,8 @@ oss_wr operator<<(oss_wr&& lhs, T rhs) {
std::cerr << "[" << lvlname << "] " << classname << "::" \ std::cerr << "[" << lvlname << "] " << classname << "::" \
<< funname << ": " << message << "\nStack trace:\n"; \ << funname << ": " << message << "\nStack trace:\n"; \
void* bt_array[20]; \ void* bt_array[20]; \
size_t size = backtrace(bt_array, 20); \ size_t size = ::cppa::detail::backtrace(bt_array, 20); \
backtrace_symbols_fd(bt_array, size, 2); \ ::cppa::detail::backtrace_symbols_fd(bt_array, size, 2); \
} CPPA_VOID_STMT } CPPA_VOID_STMT
#ifndef CPPA_LOG_LEVEL #ifndef CPPA_LOG_LEVEL
......
...@@ -94,12 +94,6 @@ class node_id : public ref_counted, util::comparable<node_id> { ...@@ -94,12 +94,6 @@ class node_id : public ref_counted, util::comparable<node_id> {
*/ */
inline const host_id_type& host_id() const { return m_host_id; } inline const host_id_type& host_id() const { return m_host_id; }
/**
* @brief Returns the proccess_information for the running process.
* @returns A pointer to the singleton of this process.
*/
static const intrusive_ptr<node_id>& get();
/** @cond PRIVATE */ /** @cond PRIVATE */
// "inherited" from comparable<node_id> // "inherited" from comparable<node_id>
......
...@@ -67,13 +67,6 @@ inline detail::empty_tuple* get_empty_tuple() { ...@@ -67,13 +67,6 @@ inline detail::empty_tuple* get_empty_tuple() {
return detail::singleton_manager::get_empty_tuple(); return detail::singleton_manager::get_empty_tuple();
} }
#ifdef CPPA_WINDOWS
inline windows::windows_tcp* get_windows_tcp() {
return detail::singleton_manager::get_windows_tcp();
}
#endif
} // namespace cppa } // namespace cppa
#endif // CPPA_SINGLETONS_HPP #endif // CPPA_SINGLETONS_HPP
...@@ -127,11 +127,8 @@ int main(int argc, char** argv) { ...@@ -127,11 +127,8 @@ int main(int argc, char** argv) {
} }
}, },
on("/quit") >> [&] { on("/quit") >> [&] {
#ifndef CPPA_WINDOWS // close STDIN; causes this match loop to quit
close(STDIN_FILENO); // close STDIN; causes this match loop to quit cin.setstate(ios_base::eofbit);
#else
cin.setstate(ios_base::eofbit); // close STDIN; causes this match loop to quit
#endif
}, },
on<string, anything>().when(_x1.starts_with("/")) >> [&] { on<string, anything>().when(_x1.starts_with("/")) >> [&] {
cout << "*** available commands:\n" cout << "*** available commands:\n"
......
...@@ -29,9 +29,12 @@ ...@@ -29,9 +29,12 @@
#include "cppa/actor.hpp" #include "cppa/actor.hpp"
#include "cppa/singletons.hpp"
#include "cppa/actor_addr.hpp" #include "cppa/actor_addr.hpp"
#include "cppa/local_actor.hpp" #include "cppa/local_actor.hpp"
#include "cppa/io/middleman.hpp"
#include "cppa/detail/raw_access.hpp" #include "cppa/detail/raw_access.hpp"
namespace cppa { namespace cppa {
...@@ -64,7 +67,7 @@ actor_id actor_addr::id() const { ...@@ -64,7 +67,7 @@ actor_id actor_addr::id() const {
} }
const node_id& actor_addr::node() const { const node_id& actor_addr::node() const {
return m_ptr ? m_ptr->node() : *node_id::get(); return m_ptr ? m_ptr->node() : *get_middleman()->node();
} }
bool actor_addr::is_remote() const { bool actor_addr::is_remote() const {
......
...@@ -72,7 +72,7 @@ actor_addr actor_namespace::read(deserializer* source) { ...@@ -72,7 +72,7 @@ actor_addr actor_namespace::read(deserializer* source) {
auto aid = source->read<uint32_t>(); // actor id auto aid = source->read<uint32_t>(); // actor id
auto pid = source->read<uint32_t>(); // process id auto pid = source->read<uint32_t>(); // process id
source->read_raw(node_id::host_id_size, hid.data()); // host id source->read_raw(node_id::host_id_size, hid.data()); // host id
auto this_node = node_id::get(); auto this_node = get_middleman()->node();
if (aid == 0 && pid == 0) { if (aid == 0 && pid == 0) {
// 0:0 identifies an invalid actor // 0:0 identifies an invalid actor
return invalid_actor_addr; return invalid_actor_addr;
......
...@@ -35,9 +35,12 @@ ...@@ -35,9 +35,12 @@
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
#include "cppa/scheduler.hpp" #include "cppa/scheduler.hpp"
#include "cppa/singletons.hpp"
#include "cppa/actor_proxy.hpp" #include "cppa/actor_proxy.hpp"
#include "cppa/exit_reason.hpp" #include "cppa/exit_reason.hpp"
#include "cppa/io/middleman.hpp" #include "cppa/io/middleman.hpp"
#include "cppa/detail/types_array.hpp" #include "cppa/detail/types_array.hpp"
#include "cppa/detail/singleton_manager.hpp" #include "cppa/detail/singleton_manager.hpp"
...@@ -46,7 +49,7 @@ using namespace std; ...@@ -46,7 +49,7 @@ using namespace std;
namespace cppa { namespace cppa {
actor_proxy::actor_proxy(actor_id mid) : super(mid) { actor_proxy::actor_proxy(actor_id mid) : super(mid) {
m_node = node_id::get(); m_node = get_middleman()->node();
} }
} // namespace cppa } // namespace cppa
/*
Copyright (c) 2012 mingw-w64 project
Contributing author: Kai Tietz
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <windows.h>
/*
#include <dbghelp.h>
*/
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
int backtrace (void **buffer, int size);
char ** backtrace_symbols (void *const *buffer, int size);
void backtrace_symbols_fd (void *const *buffer, int size, int fd);
int
backtrace (void **buffer, int size)
{
USHORT frames;
HANDLE hProcess;
if (size <= 0)
return 0;
hProcess = GetCurrentProcess ();
frames = CaptureStackBackTrace (0, (DWORD) size, buffer, NULL);
return (int) frames;
}
char **
backtrace_symbols (void *const *buffer, int size)
{
size_t t = 0;
char **r, *cur;
int i;
t = ((size_t) size) * ((sizeof (void *) * 2) + 6 + sizeof (char *));
r = (char **) malloc (t);
if (!r)
return r;
cur = (char *) &r[size];
for (i = 0; i < size; i++)
{
r[i] = cur;
sprintf (cur, "[+0x%Ix]", (size_t) buffer[i]);
cur += strlen (cur) + 1;
}
return r;
}
void
backtrace_symbols_fd (void *const *buffer, int size, int fd)
{
char s[128];
int i;
for (i = 0; i < size; i++)
{
sprintf (s, "[+0x%Ix]\n", (size_t) buffer[i]);
write (fd, s, strlen (s));
}
_commit (fd);
}
/*
void printStack (void);
void printStack (void)
{
unsigned int i;
void *stack[100];
unsigned short frames;
SYMBOL_INFO *symbol;
HANDLE hProcess;
hProcess = GetCurrentProcess ();
SymInitialize (hProcess, NULL, TRUE);
frames = CaptureStackBackTrace( 0, 100, stack, NULL );
symbol = (SYMBOL_INFO *) calloc (sizeof (SYMBOL_INFO) + 256 * sizeof (char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof (SYMBOL_INFO);
for (i = 0;i < frames; i++)
{
SymFromAddr (hProcess, (DWORD_PTR) (stack[i]), 0, symbol);
printf ("%u: %p %s = 0x%Ix\n", frames - i - 1, stack[i], symbol->Name, symbol->Address);
}
free (symbol);
}
int main ()
{
printStack ();
return 0;
}
*/
...@@ -29,19 +29,20 @@ ...@@ -29,19 +29,20 @@
#include <sstream> #include <sstream>
#include <stdlib.h>
#include "cppa/config.hpp" #include "cppa/config.hpp"
#include "cppa/exception.hpp" #include "cppa/exception.hpp"
#ifndef CPPA_WINDOWS #ifdef CPPA_WINDOWS
#include <sys/socket.h> # include <winerror.h>
#include <sys/un.h>
#else #else
#include <winerror.h> # include <errno.h>
# include <sys/socket.h>
# include <sys/un.h>
#endif #endif
#include <stdlib.h>
#include <errno.h>
namespace { namespace {
std::string ae_what(std::uint32_t reason) { std::string ae_what(std::uint32_t reason) {
...@@ -52,20 +53,20 @@ std::string ae_what(std::uint32_t reason) { ...@@ -52,20 +53,20 @@ std::string ae_what(std::uint32_t reason) {
std::string be_what(int err_code) { std::string be_what(int err_code) {
switch (err_code) { switch (err_code) {
#ifndef CPPA_WINDOWS # ifndef CPPA_WINDOWS
case EACCES: return "EACCES: address is protected; root access needed"; case EACCES: return "EACCES: address protected; root access needed";
case EADDRINUSE: return "EADDRINUSE: address is already in use"; case EADDRINUSE: return "EADDRINUSE: address already in use";
case EBADF: return "EBADF: no valid socket descriptor"; case EBADF: return "EBADF: no valid socket descriptor";
case EINVAL: return "EINVAL: socket already bound to an address"; case EINVAL: return "EINVAL: socket already bound to an address";
case ENOTSOCK: return "ENOTSOCK: file descriptor given"; case ENOTSOCK: return "ENOTSOCK: no file descriptor given";
#else # else
case WSAEACCES: return "EACCES: address is protected; root access needed"; case WSAEACCES: return "EACCES: address protected; root access needed";
case WSAEADDRINUSE: return "EADDRINUSE: address is already in use"; case WSAEADDRINUSE: return "EADDRINUSE: address already in use";
case WSAEBADF: return "EBADF: no valid socket descriptor"; case WSAEBADF: return "EBADF: no valid socket descriptor";
case WSAEINVAL: return "EINVAL: socket already bound to an address"; case WSAEINVAL: return "EINVAL: socket already bound to an address";
case WSAENOTSOCK: return "ENOTSOCK: file descriptor given"; case WSAENOTSOCK: return "ENOTSOCK: no file descriptor given";
#endif # endif
default: break; default: break;
} }
std::stringstream oss; std::stringstream oss;
oss << "an unknown error occurred (code: " << err_code << ")"; oss << "an unknown error occurred (code: " << err_code << ")";
......
...@@ -11,12 +11,11 @@ ...@@ -11,12 +11,11 @@
* * * *
* Copyright (C) 2011-2013 * * Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> * * Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* Raphael Hiesgen <raphael.hiesgen@haw-hamburg.de> *
* * * *
* This file is part of libcppa. * * This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under * * libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the * * the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation, either version 3 of the License * * Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. * * or (at your option) any later version. *
* * * *
* libcppa is distributed in the hope that it will be useful, * * libcppa is distributed in the hope that it will be useful, *
...@@ -29,44 +28,72 @@ ...@@ -29,44 +28,72 @@
\******************************************************************************/ \******************************************************************************/
#ifndef WINDOWS_TCP_HPP /******************************************************************************\
#define WINDOWS_TCP_HPP * Based on work by the mingw-w64 project; *
* original header: *
#include <atomic> * *
#include <vector> * Copyright (c) 2012 mingw-w64 project *
#include <algorithm> * *
#include <functional> * Contributing author: Kai Tietz *
* *
#include "cppa/cppa.hpp" * Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
#include <winsock2.h> * to deal in the Software without restriction, including without limitation *
#include <ws2tcpip.h> * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
#include "cppa/windows/windows_tcp.hpp" * and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
#include "cppa/detail/singleton_mixin.hpp" * The above copyright notice and this permission notice shall be included in *
#include "cppa/detail/singleton_manager.hpp" * all copies or substantial portions of the Software. *
* *
namespace cppa { namespace windows { * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
class windows_tcp { * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
friend class detail::singleton_manager; * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
* DEALINGS IN THE SOFTWARE. *
\******************************************************************************/
private:
static inline windows_tcp* create_singleton() { #include <windows.h>
return new windows_tcp; #include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include "cppa/detail/execinfo_windows.hpp"
namespace cppa {
namespace detail {
int backtrace(void** buffer, int size) {
if (size <= 0) return 0;
auto hProcess = GetCurrentProcess();
auto frames = CaptureStackBackTrace (0, (DWORD) size, buffer, NULL);
return static_cast<int>(frames);
}
char** backtrace_symbols(void* const* buffer, int size) {
auto t = ((size_t) size) * ((sizeof(void*) * 2) + 6 + sizeof(char*));
auto r = (char**) malloc(t);
if (!r) return nullptr;
auto cur = (char *) &r[size];
for (int i = 0; i < size; i++) {
r[i] = cur;
sprintf (cur, "[+0x%Ix]", (size_t) buffer[i]);
cur += strlen (cur) + 1;
} }
return r;
}
void backtrace_symbols_fd(void* const* buffer, int size, int fd) {
char s[128];
for (int i = 0; i < size; i++) {
sprintf(s, "[+0x%Ix]\n", (size_t) buffer[i]);
write(fd, s, strlen (s));
}
_commit(fd);
}
void initialize(); } // namespace detail
void dispose(); } // namespace cppa
void destroy();
};
windows_tcp* get_windows_tcp();
} } // namespace cppa::windows
#endif // WINDOWS_TCP_HPP
This diff is collapsed.
...@@ -185,13 +185,19 @@ std::vector<std::string> get_mac_addresses() { ...@@ -185,13 +185,19 @@ std::vector<std::string> get_mac_addresses() {
// Link with Iphlpapi.lib // Link with Iphlpapi.lib
// #pragma comment(lib, "IPHLPAPI.lib") -liphlpapi // #pragma comment(lib, "IPHLPAPI.lib") -liphlpapi
#define WORKING_BUFFER_SIZE 15000 namespace {
#define MAX_TRIES 3
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) constexpr size_t working_buffer_size = 15 * 1024; // 15kb by default
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) constexpr size_t max_iterations = 3;
/* Note: could also use malloc() and free() */ struct c_free {
template<typename T>
void operator()(T* ptr) {
free(ptr);
}
};
} // namespace <anonymous>
using namespace std; using namespace std;
...@@ -199,95 +205,55 @@ using namespace std; ...@@ -199,95 +205,55 @@ using namespace std;
namespace cppa { namespace util { namespace cppa { namespace util {
std::vector<std::string> get_mac_addresses() { std::vector<std::string> get_mac_addresses() {
// result vector
/* Declare and initialize variables */ vector<string> hw_addresses;
// flags to pass to GetAdaptersAddresses
DWORD dwRetVal = 0;
unsigned int i = 0;
// Set the flags to pass to GetAdaptersAddresses
ULONG flags = GAA_FLAG_INCLUDE_PREFIX; ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
// default to unspecified address family (both) // default to unspecified address family (both)
ULONG family = AF_UNSPEC; ULONG family = AF_UNSPEC;
// buffer
std::unique_ptr<IP_ADAPTER_ADDRESSES, c_free> addresses;
PIP_ADAPTER_ADDRESSES pAddresses = NULL; // init buf size to default, adjusted by GetAdaptersAddresses if needed
ULONG outBufLen = 0; ULONG addresses_len = working_buffer_size;
ULONG Iterations = 0; // stores result of latest system call
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; DWORD res = 0;
// break condition
vector<string> hw_addresses; size_t iterations = 0;
// Allocate a 15 KB buffer to start with.
outBufLen = WORKING_BUFFER_SIZE;
cppa::windows::get_windows_tcp();
do { do {
addresses.reset((IP_ADAPTER_ADDRESSES*) malloc(addresses_len));
pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen); if (!addresses) {
if (pAddresses == NULL) {
perror("Memory allocation failed for IP_ADAPTER_ADDRESSES struct"); perror("Memory allocation failed for IP_ADAPTER_ADDRESSES struct");
exit(1); exit(1);
} }
res = GetAdaptersAddresses(family, flags, nullptr,
dwRetVal = addresses.get(), &addresses_len);
GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); } while ((res == ERROR_BUFFER_OVERFLOW) && (++iterations < max_iterations));
if (res == NO_ERROR) {
if (dwRetVal == ERROR_BUFFER_OVERFLOW) { // read hardware addresses from the output we've received
FREE(pAddresses); for (auto addr = addresses.get(); addr != nullptr; addr = addr->Next) {
pAddresses = NULL; if (addr->PhysicalAddressLength > 0) {
} else {
break;
}
Iterations++;
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
if (dwRetVal == NO_ERROR) {
// If successful, output some information from the data we received
pCurrAddresses = pAddresses;
while (pCurrAddresses) {
if (pCurrAddresses->PhysicalAddressLength != 0) {
std::ostringstream oss; std::ostringstream oss;
oss << hex; oss << hex;
for (i = 0; i < (unsigned int) pCurrAddresses->PhysicalAddressLength; i++) { oss.width(2);
if (i == (pCurrAddresses->PhysicalAddressLength - 1)) { oss << static_cast<int>(addr->PhysicalAddress[0]);
oss.width(2); for (DWORD i = 1; i < addr->PhysicalAddressLength; ++i) {
oss << ((int) pCurrAddresses->PhysicalAddress[i]); oss << ":";
} else { oss.width(2);
oss.width(2); oss << static_cast<int>(addr->PhysicalAddress[i]);
oss << ((int) pCurrAddresses->PhysicalAddress[i]) << ":";
}
} }
auto addr = oss.str(); auto hw_addr = oss.str();
if (addr != "00:00:00:00:00:00") { if (hw_addr != "00:00:00:00:00:00") {
hw_addresses.push_back(std::move(addr)); hw_addresses.push_back(std::move(hw_addr));
} }
} }
pCurrAddresses = pCurrAddresses->Next;
} }
} else { } else {
if (res == ERROR_NO_DATA) {
if (dwRetVal == ERROR_NO_DATA) {
perror("No addresses were found for the requested parameters"); perror("No addresses were found for the requested parameters");
} else { } else {
perror("Call to GetAdaptersAddresses failed with error"); perror("Call to GetAdaptersAddresses failed with error");
} }
} }
if (pAddresses) {
FREE(pAddresses);
}
return hw_addresses; return hw_addresses;
} }
......
This diff is collapsed.
...@@ -290,7 +290,7 @@ class local_group_module : public abstract_group::module { ...@@ -290,7 +290,7 @@ class local_group_module : public abstract_group::module {
public: public:
local_group_module() local_group_module()
: super("local"), m_process(node_id::get()) : super("local"), m_process(get_middleman()->node())
, m_actor_utype(uniform_typeid<actor>()){ } , m_actor_utype(uniform_typeid<actor>()){ }
group get(const string& identifier) override { group get(const string& identifier) override {
......
...@@ -26,17 +26,13 @@ ...@@ -26,17 +26,13 @@
* You should have received a copy of the GNU Lesser General Public License * * You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. * * along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/ \******************************************************************************/
#include "cppa/config.hpp"
#ifdef CPPA_WINDOWS
# include <ws2tcpip.h>
# include <winsock2.h>
#endif
#include <ios> #include <ios>
#include <cstring> #include <cstring>
#include <errno.h> #include <errno.h>
#include <iostream> #include <iostream>
#include "cppa/config.hpp"
#include "cppa/logging.hpp" #include "cppa/logging.hpp"
#include "cppa/exception.hpp" #include "cppa/exception.hpp"
...@@ -86,38 +82,48 @@ struct socket_guard { ...@@ -86,38 +82,48 @@ struct socket_guard {
}; };
#ifdef CPPA_WINDOWS
inline native_socket_type do_accept(native_socket_type fd,
sockaddr* addr,
socklen_t* addrlen) {
return ::WSAAccept(fd, addr, addrlen, 0, 0)
}
#else
inline native_socket_type do_accept(native_socket_type fd,
sockaddr* addr,
socklen_t* addrlen) {
return ::accept(fd, addr, addrlen);
}
#endif
bool accept_impl(stream_ptr_pair& result, bool accept_impl(stream_ptr_pair& result,
native_socket_type fd, native_socket_type fd,
bool nonblocking) { bool nonblocking) {
/*
sockaddr addr;
memset(&addr, 0, sizeof(addr));
*/
//socklen_t addrlen = sizeof(addr);
/*
socklen_t addrlen;
memset(&addrlen, 0, sizeof(addrlen));
# ifdef CPPA_WINDOWS
addrlen = sizeof(addr);
# endif
*/
sockaddr addr; sockaddr addr;
#ifdef CPPA_WINDOWS
int addrlen;
#else
socklen_t addrlen; socklen_t addrlen;
#endif
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
#ifdef CPPA_WINDOWS
addrlen=sizeof(addr); // check this
auto sfd = ::WSAAccept(fd, &addr, &addrlen, 0, 0); // size too small
if (sfd == INVALID_SOCKET) {
if (nonblocking && (WSAGetLastError() == WSATRY_AGAIN || WSAGetLastError() == WSAEWOULDBLOCK)) {
// ok, try again
return false;
}
throw_io_failure("accept failed");
}
#else
memset(&addrlen, 0, sizeof(addrlen)); memset(&addrlen, 0, sizeof(addrlen));
auto sfd = ::accept(fd, &addr, &addrlen); auto sfd = do_accept(fd, &addr, &addrlen);
if (sfd < 0) { if (sfd == invalid_socket) {
if (nonblocking && (errno == EAGAIN || errno == EWOULDBLOCK)) { auto err = last_socket_error();
if (nonblocking && would_block_or_temporarily_unavailable(err)) {
// ok, try again // ok, try again
return false; return false;
} }
throw_io_failure("accept failed"); throw_io_failure("accept failed");
} }
#endif
stream_ptr ptr(ipv4_io_stream::from_native_socket(sfd)); stream_ptr ptr(ipv4_io_stream::from_native_socket(sfd));
result.first = ptr; result.first = ptr;
result.second = ptr; result.second = ptr;
...@@ -133,55 +139,32 @@ std::unique_ptr<acceptor> ipv4_acceptor::create(std::uint16_t port, ...@@ -133,55 +139,32 @@ std::unique_ptr<acceptor> ipv4_acceptor::create(std::uint16_t port,
const char* addr) { const char* addr) {
CPPA_LOGM_TRACE("ipv4_acceptor", CPPA_ARG(port) << ", addr = " CPPA_LOGM_TRACE("ipv4_acceptor", CPPA_ARG(port) << ", addr = "
<< (addr ? addr : "nullptr")); << (addr ? addr : "nullptr"));
native_socket_type sockfd; # ifdef CPPA_WINDOWS
// ensure that TCP has been initialized via WSAStartup
#ifdef CPPA_WINDOWS cppa::get_middleman();
// ensure tcp has been initialized # endif
cppa::windows::get_windows_tcp(); native_socket_type sockfd = socket(AF_INET, SOCK_STREAM, 0);
sockfd = INVALID_SOCKET;
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd == INVALID_SOCKET) {
throw network_error("could not create server socket");
}
#else
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == invalid_socket) { if (sockfd == invalid_socket) {
throw network_error("could not create server socket"); throw network_error("could not create server socket");
} }
#endif
// sguard closes the socket in case of exception // sguard closes the socket in case of exception
socket_guard sguard(sockfd); socket_guard sguard(sockfd);
#ifdef CPPA_WINDOWS
int on = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on,sizeof(on)) < 0) {
throw_io_failure("unable to set SO_REUSEADDR");
}
#else
int on = 1; int on = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<setsockopt_ptr>(&on), sizeof(on)) < 0) {
throw_io_failure("unable to set SO_REUSEADDR"); throw_io_failure("unable to set SO_REUSEADDR");
} }
#endif
struct sockaddr_in serv_addr; struct sockaddr_in serv_addr;
memset((char*) &serv_addr, 0, sizeof(serv_addr)); memset((char*) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET; serv_addr.sin_family = AF_INET;
if (! addr) { if (! addr) {
serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_addr.s_addr = INADDR_ANY;
} }
#ifdef CPPA_WINDOWS
else if (::inet_pton(AF_INET, addr, &serv_addr.sin_addr) <= 0) { else if (::inet_pton(AF_INET, addr, &serv_addr.sin_addr) <= 0) {
throw network_error("invalid IPv4 address"); throw network_error("invalid IPv4 address");
} }
#else
else if (inet_pton(AF_INET, addr, &serv_addr.sin_addr) <= 0) {
throw network_error("invalid IPv4 address");
}
#endif
serv_addr.sin_port = htons(port); serv_addr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) { if (bind(sockfd, (sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) {
throw bind_failure(errno); throw bind_failure(errno);
} }
if (listen(sockfd, 10) != 0) { if (listen(sockfd, 10) != 0) {
......
...@@ -34,19 +34,14 @@ ...@@ -34,19 +34,14 @@
#include <iostream> #include <iostream>
#include "cppa/config.hpp" #include "cppa/config.hpp"
#ifdef CPPA_WINDOWS
# include <ws2tcpip.h>
# include <winsock2.h>
#endif
#include "cppa/logging.hpp" #include "cppa/logging.hpp"
#include "cppa/exception.hpp" #include "cppa/exception.hpp"
#include "cppa/detail/fd_util.hpp" #include "cppa/detail/fd_util.hpp"
#include "cppa/io/ipv4_io_stream.hpp" #include "cppa/io/ipv4_io_stream.hpp"
#ifdef CPPA_WINDOWS #ifdef CPPA_WINDOWS
# include "cppa/singletons.hpp" # include <ws2tcpip.h>
# include "cppa/windows/windows_tcp.hpp" # include <winsock2.h>
#else #else
# include <netdb.h> # include <netdb.h>
# include <unistd.h> # include <unistd.h>
...@@ -56,9 +51,6 @@ ...@@ -56,9 +51,6 @@
# include <netinet/tcp.h> # include <netinet/tcp.h>
#endif #endif
namespace cppa { namespace io { namespace cppa { namespace io {
using namespace ::cppa::detail::fd_util; using namespace ::cppa::detail::fd_util;
...@@ -140,14 +132,12 @@ io::stream_ptr ipv4_io_stream::connect_to(const char* host, ...@@ -140,14 +132,12 @@ io::stream_ptr ipv4_io_stream::connect_to(const char* host,
std::uint16_t port) { std::uint16_t port) {
CPPA_LOGF_TRACE(CPPA_ARG(host) << ", " << CPPA_ARG(port)); CPPA_LOGF_TRACE(CPPA_ARG(host) << ", " << CPPA_ARG(port));
CPPA_LOGF_INFO("try to connect to " << host << " on port " << port); CPPA_LOGF_INFO("try to connect to " << host << " on port " << port);
struct sockaddr_in serv_addr; # ifdef CPPA_WINDOWS
struct hostent* server; // make sure TCP has been initialized via WSAStartup
cppa::get_middleman();
#ifdef CPPA_WINDOWS # endif
// ensure tcp has been initialized sockaddr_in serv_addr;
cppa::get_windows_tcp(); hostent* server;
#endif
native_socket_type fd = socket(AF_INET, SOCK_STREAM, 0); native_socket_type fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == invalid_socket) { if (fd == invalid_socket) {
throw network_error("socket creation failed"); throw network_error("socket creation failed");
......
...@@ -77,7 +77,7 @@ local_actor::local_actor() ...@@ -77,7 +77,7 @@ local_actor::local_actor()
, m_dummy_node(), m_current_node(&m_dummy_node) , m_dummy_node(), m_current_node(&m_dummy_node)
, m_planned_exit_reason(exit_reason::not_exited) , m_planned_exit_reason(exit_reason::not_exited)
, m_state(actor_state::ready) { , m_state(actor_state::ready) {
m_node = node_id::get(); m_node = get_middleman()->node();
} }
local_actor::~local_actor() { } local_actor::~local_actor() { }
......
...@@ -49,6 +49,10 @@ ...@@ -49,6 +49,10 @@
#include "cppa/binary_deserializer.hpp" #include "cppa/binary_deserializer.hpp"
#include "cppa/util/buffer.hpp" #include "cppa/util/buffer.hpp"
#include "cppa/util/algorithm.hpp"
#include "cppa/util/ripemd_160.hpp"
#include "cppa/util/get_root_uuid.hpp"
#include "cppa/util/get_mac_addresses.hpp"
#include "cppa/io/peer.hpp" #include "cppa/io/peer.hpp"
#include "cppa/io/acceptor.hpp" #include "cppa/io/acceptor.hpp"
...@@ -66,14 +70,48 @@ ...@@ -66,14 +70,48 @@
#include "cppa/intrusive/single_reader_queue.hpp" #include "cppa/intrusive/single_reader_queue.hpp"
#ifdef CPPA_WINDOWS #ifdef CPPA_WINDOWS
#include "io.h" # include <io.h>
#include "fcntl.h" # include <fcntl.h>
#endif #endif
using namespace std; using namespace std;
namespace cppa { namespace io { namespace cppa { namespace io {
void notify_queue_event(native_socket_type fd) {
uint8_t dummy = 0;
// on unix, we have file handles, on windows, we actually have sockets
# ifdef CPPA_WINDOWS
auto res = ::send(fd, &dummy, sizeof(dummy), 0);
# else
auto res = ::write(fd, &dummy, sizeof(dummy));
# endif
// ignore result: an "error" means our middleman has been shut down
static_cast<void>(res);
}
size_t num_queue_events(native_socket_type fd) {
static constexpr size_t num_dummies = 64;
uint8_t dummies[num_dummies];
// on unix, we have file handles, on windows, we actually have sockets
# ifdef CPPA_WINDOWS
auto read_result = ::recv(fd, dummies, num_dummies, 0);
# else
auto read_result = ::read(fd, dummies, num_dummies);
# endif
if (read_result < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// try again later
return 0;
}
else {
CPPA_LOGF_ERROR("cannot read from pipe");
CPPA_CRITICAL("cannot read from pipe");
}
}
return static_cast<size_t>(read_result);
}
class middleman_event { class middleman_event {
friend class intrusive::single_reader_queue<middleman_event>; friend class intrusive::single_reader_queue<middleman_event>;
...@@ -132,31 +170,12 @@ class middleman_impl : public middleman { ...@@ -132,31 +170,12 @@ class middleman_impl : public middleman {
public: public:
middleman_impl() : m_done(false) { middleman_impl() : m_done(false) { }
m_handler = middleman_event_handler::create();
m_namespace.set_proxy_factory([=](actor_id aid, node_id_ptr ptr) {
return make_counted<remote_actor_proxy>(aid, std::move(ptr), this);
});
m_namespace.set_new_element_callback([=](actor_id aid, const node_id& node) {
deliver(node,
{invalid_actor_addr, nullptr},
make_any_tuple(atom("MONITOR"),
node_id::get(),
aid));
});
}
void run_later(function<void()> fun) override { void run_later(function<void()> fun) override {
m_queue.enqueue(new middleman_event(move(fun))); m_queue.enqueue(new middleman_event(move(fun)));
atomic_thread_fence(memory_order_seq_cst); atomic_thread_fence(memory_order_seq_cst);
uint8_t dummy = 0; notify_queue_event(m_pipe_in);
// ignore result; write error only means middleman already exited
#ifdef CPPA_WINDOWS
auto res = ::send(m_pipe_write, (char *)&dummy, sizeof(dummy), 0);
#else
auto res = write(m_pipe_write, &dummy, sizeof(dummy));
#endif
static_cast<void>(res);
} }
bool register_peer(const node_id& node, peer* ptr) override { bool register_peer(const node_id& node, peer* ptr) override {
...@@ -253,11 +272,11 @@ class middleman_impl : public middleman { ...@@ -253,11 +272,11 @@ class middleman_impl : public middleman {
} }
} }
void register_acceptor(const actor_addr& whom, peer_acceptor* ptr) override { void register_acceptor(const actor_addr& aa, peer_acceptor* ptr) override {
run_later([=] { run_later([=] {
CPPA_LOGC_TRACE("cppa::io::middleman", CPPA_LOGC_TRACE("cppa::io::middleman",
"register_acceptor$lambda", ""); "register_acceptor$lambda", "");
m_acceptors[whom].push_back(ptr); m_acceptors[aa].push_back(ptr);
continue_reader(ptr); continue_reader(ptr);
}); });
} }
...@@ -265,19 +284,29 @@ class middleman_impl : public middleman { ...@@ -265,19 +284,29 @@ class middleman_impl : public middleman {
protected: protected:
void initialize() override { void initialize() override {
#ifdef CPPA_WINDOWS # ifdef CPPA_WINDOWS
// if ( CreatePipe(&m_pipe_read,&m_pipe_write,0,4096) ) { CPPA_CRITICAL("cannot create pipe"); } WSADATA WinsockData;
// DWORD dwMode = PIPE_NOWAIT; if (WSAStartup(MAKEWORD(2, 2), &WinsockData) != 0) {
// if ( !SetNamedPipeHandleState(&m_pipe_read,&dwMode,NULL,NULL) ) { CPPA_CRITICAL("cannot set PIPE_NOWAIT"); } CPPA_CRITICAL("WSAStartup failed");
native_socket_type pipefds[2]; }
if ( cppa::io::dumb_socketpair(pipefds, 0) != 0) { CPPA_CRITICAL("cannot create pipe"); } # endif
#else m_node = compute_node_id();
int pipefds[2]; m_handler = middleman_event_handler::create();
if (pipe(pipefds) != 0) { CPPA_CRITICAL("cannot create pipe"); } m_namespace.set_proxy_factory([=](actor_id aid, node_id_ptr ptr) {
#endif return make_counted<remote_actor_proxy>(aid, std::move(ptr), this);
m_pipe_read = pipefds[0]; });
m_pipe_write = pipefds[1]; m_namespace.set_new_element_callback([=](actor_id aid,
detail::fd_util::nonblocking(m_pipe_read, true); const node_id& node) {
deliver(node,
{invalid_actor_addr, nullptr},
make_any_tuple(atom("MONITOR"),
m_node,
aid));
});
auto pipefds = detail::fd_util::create_pipe();
m_pipe_out = pipefds.first;
m_pipe_in = pipefds.second;
detail::fd_util::nonblocking(m_pipe_out, true);
// start threads // start threads
m_thread = thread([this] { middleman_loop(this); }); m_thread = thread([this] { middleman_loop(this); });
} }
...@@ -288,14 +317,25 @@ class middleman_impl : public middleman { ...@@ -288,14 +317,25 @@ class middleman_impl : public middleman {
this->m_done = true; this->m_done = true;
}); });
m_thread.join(); m_thread.join();
closesocket(m_pipe_out);
closesocket(m_pipe_read); closesocket(m_pipe_in);
closesocket(m_pipe_write); # ifdef CPPA_WINDOWS
# WSACleanup();
# endif
} }
private: private:
static cppa::node_id_ptr compute_node_id() {
using namespace cppa::util;
auto macs = util::get_mac_addresses();
auto hd_serial_and_mac_addr = util::join(macs.begin(), macs.end())
+ util::get_root_uuid();
cppa::node_id::host_id_type node_id;
ripemd_160(node_id, hd_serial_and_mac_addr);
return new cppa::node_id(getpid(), node_id);
}
inline void quit() { m_done = true; } inline void quit() { m_done = true; }
inline bool done() const { return m_done; } inline bool done() const { return m_done; }
...@@ -305,9 +345,8 @@ class middleman_impl : public middleman { ...@@ -305,9 +345,8 @@ class middleman_impl : public middleman {
middleman_event_handler& handler(); middleman_event_handler& handler();
thread m_thread; thread m_thread;
native_socket_type m_pipe_out;
native_socket_type m_pipe_read; native_socket_type m_pipe_in;
native_socket_type m_pipe_write;
middleman_queue m_queue; middleman_queue m_queue;
struct peer_entry { struct peer_entry {
...@@ -335,39 +374,12 @@ class middleman_overseer : public continuable { ...@@ -335,39 +374,12 @@ class middleman_overseer : public continuable {
continue_reading_result continue_reading() { continue_reading_result continue_reading() {
CPPA_LOG_TRACE(""); CPPA_LOG_TRACE("");
static constexpr size_t num_dummies = 64; // on MacOS, recv() on a pipe fd will fail,
uint8_t dummies[num_dummies]; // on Windows, our pipe is actually composed of two sockets
#ifdef CPPA_WINDOWS // and there's no read() function to read from sockets
auto read_result = ::recv(read_handle(), (char *)dummies, num_dummies, 0); auto events = num_queue_events(read_handle());
#else CPPA_LOG_DEBUG("read " << events << " messages from queue");
auto read_result = ::read(read_handle(), dummies, num_dummies); for (size_t i = 0; i < events; ++i) {
#endif
CPPA_LOG_DEBUG("read " << read_result << " messages from queue");
#ifdef CPPA_WINDOWS
if (read_result == SOCKET_ERROR) {
if (WSAGetLastError() == WSAEWOULDBLOCK) {
// try again later
return read_continue_later;
}
else {
CPPA_LOG_ERROR("cannot read from pipe");
CPPA_CRITICAL("cannot read from pipe");
}
}
#else
if (read_result < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// try again later
return read_continue_later;
}
else {
CPPA_LOG_ERROR("cannot read from pipe");
CPPA_CRITICAL("cannot read from pipe");
}
}
#endif
for (int i = 0; i < read_result; ++i) {
unique_ptr<middleman_event> msg(m_queue.try_pop()); unique_ptr<middleman_event> msg(m_queue.try_pop());
if (!msg) { if (!msg) {
CPPA_LOG_ERROR("nullptr dequeued"); CPPA_LOG_ERROR("nullptr dequeued");
...@@ -395,9 +407,10 @@ void middleman_loop(middleman_impl* impl) { ...@@ -395,9 +407,10 @@ void middleman_loop(middleman_impl* impl) {
middleman_event_handler* handler = impl->m_handler.get(); middleman_event_handler* handler = impl->m_handler.get();
CPPA_LOGF_TRACE("run middleman loop"); CPPA_LOGF_TRACE("run middleman loop");
CPPA_LOGF_INFO("middleman runs at " CPPA_LOGF_INFO("middleman runs at "
<< to_string(*node_id::get())); << to_string(impl->node()));
handler->init(); handler->init();
impl->continue_reader(new middleman_overseer(impl->m_pipe_read, impl->m_queue)); impl->continue_reader(new middleman_overseer(impl->m_pipe_out,
impl->m_queue));
handler->update(); handler->update();
while (!impl->done()) { while (!impl->done()) {
handler->poll([&](event_bitmask mask, continuable* io) { handler->poll([&](event_bitmask mask, continuable* io) {
......
...@@ -38,8 +38,11 @@ ...@@ -38,8 +38,11 @@
#include "cppa/config.hpp" #include "cppa/config.hpp"
#include "cppa/node_id.hpp" #include "cppa/node_id.hpp"
#include "cppa/serializer.hpp" #include "cppa/serializer.hpp"
#include "cppa/singletons.hpp"
#include "cppa/primitive_variant.hpp" #include "cppa/primitive_variant.hpp"
#include "cppa/io/middleman.hpp"
#include "cppa/util/algorithm.hpp" #include "cppa/util/algorithm.hpp"
#include "cppa/util/ripemd_160.hpp" #include "cppa/util/ripemd_160.hpp"
#include "cppa/util/get_root_uuid.hpp" #include "cppa/util/get_root_uuid.hpp"
...@@ -47,27 +50,6 @@ ...@@ -47,27 +50,6 @@
namespace { namespace {
cppa::node_id* compute_proc_info() {
using namespace cppa::util;
auto macs = get_mac_addresses();
auto hd_serial_and_mac_addr = join(macs.begin(), macs.end())
+ get_root_uuid();
cppa::node_id::host_id_type node_id;
ripemd_160(node_id, hd_serial_and_mac_addr);
return new cppa::node_id(getpid(), node_id);
}
cppa::node_id_ptr s_pinfo;
struct pinfo_manager {
pinfo_manager() {
if (!s_pinfo) {
s_pinfo.reset(compute_proc_info());
}
}
}
s_pinfo_manager;
std::uint8_t hex_char_value(char c) { std::uint8_t hex_char_value(char c) {
if (isdigit(c)) { if (isdigit(c)) {
return static_cast<std::uint8_t>(c - '0'); return static_cast<std::uint8_t>(c - '0');
...@@ -146,10 +128,6 @@ std::string to_string(const node_id::host_id_type& node_id) { ...@@ -146,10 +128,6 @@ std::string to_string(const node_id::host_id_type& node_id) {
return oss.str(); return oss.str();
} }
const intrusive_ptr<node_id>& node_id::get() {
return s_pinfo;
}
int node_id::compare(const node_id& other) const { int node_id::compare(const node_id& other) const {
int tmp = strncmp(reinterpret_cast<const char*>(host_id().data()), int tmp = strncmp(reinterpret_cast<const char*>(host_id().data()),
reinterpret_cast<const char*>(other.host_id().data()), reinterpret_cast<const char*>(other.host_id().data()),
......
...@@ -112,7 +112,7 @@ continue_reading_result peer::continue_reading() { ...@@ -112,7 +112,7 @@ continue_reading_result peer::continue_reading() {
memcpy(host_id.data(), m_rd_buf.offset_data(sizeof(uint32_t)), memcpy(host_id.data(), m_rd_buf.offset_data(sizeof(uint32_t)),
node_id::host_id_size); node_id::host_id_size);
m_node.reset(new node_id(process_id, host_id)); m_node.reset(new node_id(process_id, host_id));
if (*node_id::get() == *m_node) { if (*parent()->node() == *m_node) {
std::cerr << "*** middleman warning: " std::cerr << "*** middleman warning: "
"incoming connection from self" "incoming connection from self"
<< std::endl; << std::endl;
...@@ -203,7 +203,7 @@ void peer::monitor(const actor_addr&, ...@@ -203,7 +203,7 @@ void peer::monitor(const actor_addr&,
return; return;
} }
auto entry = get_actor_registry()->get_entry(aid); auto entry = get_actor_registry()->get_entry(aid);
auto pself = node_id::get(); auto pself = parent()->node();
if (*node == *pself) { if (*node == *pself) {
CPPA_LOG_ERROR("received 'MONITOR' from pself"); CPPA_LOG_ERROR("received 'MONITOR' from pself");
......
...@@ -63,7 +63,7 @@ continue_reading_result peer_acceptor::continue_reading() { ...@@ -63,7 +63,7 @@ continue_reading_result peer_acceptor::continue_reading() {
} }
if (opt) { if (opt) {
auto& pair = *opt; auto& pair = *opt;
auto& pself = node_id::get(); auto& pself = m_parent->node();
uint32_t process_id = pself->process_id(); uint32_t process_id = pself->process_id();
try { try {
auto& out = pair.second; auto& out = pair.second;
......
...@@ -56,18 +56,6 @@ class opencl_metainfo : public detail::singleton_mixin<opencl_metainfo> { }; ...@@ -56,18 +56,6 @@ class opencl_metainfo : public detail::singleton_mixin<opencl_metainfo> { };
} } // namespace cppa::opencl } } // namespace cppa::opencl
#endif #endif
#ifdef CPPA_WINDOWS
# include "cppa/windows/windows_tcp.hpp"
#else
namespace cppa { namespace windows {
class windows_tcp : public detail::singleton_mixin<windows_tcp> { };
} } // namespace cppa::windows
#endif
namespace cppa { void shutdown() { detail::singleton_manager::shutdown(); } } namespace cppa { void shutdown() { detail::singleton_manager::shutdown(); } }
namespace cppa { namespace detail { namespace cppa { namespace detail {
...@@ -82,7 +70,6 @@ std::atomic<group_manager*> s_group_manager; ...@@ -82,7 +70,6 @@ std::atomic<group_manager*> s_group_manager;
std::atomic<empty_tuple*> s_empty_tuple; std::atomic<empty_tuple*> s_empty_tuple;
std::atomic<scheduler*> s_scheduler; std::atomic<scheduler*> s_scheduler;
std::atomic<logging*> s_logger; std::atomic<logging*> s_logger;
std::atomic<windows::windows_tcp*> s_windows_tcp;
} // namespace <anonymous> } // namespace <anonymous>
...@@ -104,11 +91,6 @@ void singleton_manager::shutdown() { ...@@ -104,11 +91,6 @@ void singleton_manager::shutdown() {
CPPA_LOGF_DEBUG("clear type info map"); CPPA_LOGF_DEBUG("clear type info map");
destroy(s_uniform_type_info_map); destroy(s_uniform_type_info_map);
destroy(s_logger); destroy(s_logger);
#ifdef CPPA_WINDOWS
CPPA_LOGF_DEBUG("WSACleanup");
destroy(s_windows_tcp);
#endif
} }
opencl::opencl_metainfo* singleton_manager::get_opencl_metainfo() { opencl::opencl_metainfo* singleton_manager::get_opencl_metainfo() {
...@@ -120,15 +102,6 @@ opencl::opencl_metainfo* singleton_manager::get_opencl_metainfo() { ...@@ -120,15 +102,6 @@ opencl::opencl_metainfo* singleton_manager::get_opencl_metainfo() {
# endif # endif
} }
windows::windows_tcp* singleton_manager::get_windows_tcp() {
#ifdef CPPA_WINDOWS
return lazy_get(s_windows_tcp);
# else
CPPA_LOGF_ERROR("libcppa was compiled without windows support");
throw std::logic_error("libcppa was compiled without windows support");
# endif
}
actor_registry* singleton_manager::get_actor_registry() { actor_registry* singleton_manager::get_actor_registry() {
return lazy_get(s_actor_registry); return lazy_get(s_actor_registry);
......
/* socketpair.c
Copyright 2007, 2010 by Nathan C. Myers <ncm@cantrip.org>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
The name of the author must not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Changes:
* 2014-02-12: merge David Woodhouse, Ger Hobbelt improvements
* git.infradead.org/users/dwmw2/openconnect.git/commitdiff/bdeefa54
* github.com/GerHobbelt/selectable-socketpair
* always init the socks[] to -1/INVALID_SOCKET on error, both on Win32/64
* and UNIX/other platforms
* 2013-07-18: Change to BSD 3-clause license
* 2010-03-31:
* set addr to 127.0.0.1 because win32 getsockname does not always set it.
* 2010-02-25:
* set SO_REUSEADDR option to avoid leaking some windows resource.
* Windows System Error 10049, "Event ID 4226 TCP/IP has reached
* the security limit imposed on the number of concurrent TCP connect
* attempts." Bleah.
* 2007-04-25:
* preserve value of WSAGetLastError() on all error returns.
* 2007-04-22: (Thanks to Matthew Gregan <kinetik@flim.org>)
* s/EINVAL/WSAEINVAL/ fix trivial compile failure
* s/socket/WSASocket/ enable creation of sockets suitable as stdin/stdout
* of a child process.
* add argument make_overlapped
*/
#include <string.h>
#ifdef WIN32
# include <ws2tcpip.h> /* socklen_t, et al (MSVC20xx) */
# include <windows.h>
# include <io.h>
# include "cppa/singletons.hpp"
# include "cppa/windows/windows_tcp.hpp"
#else
# include <sys/types.h>
# include <sys/socket.h>
# include <errno.h>
#endif
namespace cppa { namespace io {
#ifdef WIN32
/* dumb_socketpair:
* If make_overlapped is nonzero, both sockets created will be usable for
* "overlapped" operations via WSASend etc. If make_overlapped is zero,
* socks[0] (only) will be usable with regular ReadFile etc., and thus
* suitable for use as stdin or stdout of a child process. Note that the
* sockets must be closed with closesocket() regardless.
*/
int dumb_socketpair(SOCKET socks[2], int make_overlapped)
{
union {
struct sockaddr_in inaddr;
struct sockaddr addr;
} a;
cppa::windows::get_windows_tcp();
SOCKET listener = INVALID_SOCKET;
int e;
socklen_t addrlen = sizeof(a.inaddr);
DWORD flags = (make_overlapped ? WSA_FLAG_OVERLAPPED : 0);
int reuse = 1;
if (socks == 0) {
WSASetLastError(WSAEINVAL);
return SOCKET_ERROR;
}
socks[0] = socks[1] = -1;
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == INVALID_SOCKET) // -1
return SOCKET_ERROR;
memset(&a, 0, sizeof(a));
a.inaddr.sin_family = AF_INET;
a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.inaddr.sin_port = 0;
for (;;) {
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
(char*) &reuse, (socklen_t) sizeof(reuse)) == -1)
break;
if (bind(listener, &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
break;
memset(&a, 0, sizeof(a));
if (getsockname(listener, &a.addr, &addrlen) == SOCKET_ERROR)
break;
// win32 getsockname may only set the port number, p=0.0005.
// ( http://msdn.microsoft.com/library/ms738543.aspx ):
a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
a.inaddr.sin_family = AF_INET;
if (listen(listener, 1) == SOCKET_ERROR)
break;
socks[0] = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags);
if (socks[0] == INVALID_SOCKET)
break;
if (connect(socks[0], &a.addr, sizeof(a.inaddr)) == SOCKET_ERROR)
break;
socks[1] = accept(listener, NULL, NULL);
if (socks[1] == INVALID_SOCKET)
break;
closesocket(listener);
return 0;
}
e = WSAGetLastError();
closesocket(listener);
closesocket(socks[0]);
closesocket(socks[1]);
WSASetLastError(e);
socks[0] = socks[1] = -1;
return SOCKET_ERROR;
}
#else
int dumb_socketpair(int socks[2], int dummy)
{
if (socks == 0) {
errno = EINVAL;
return -1;
}
dummy = socketpair(AF_LOCAL, SOCK_STREAM, 0, socks);
if (dummy)
socks[0] = socks[1] = -1;
return dummy;
}
#endif
}}
\ No newline at end of file
...@@ -112,7 +112,8 @@ void publish_impl(abstract_actor_ptr ptr, std::unique_ptr<acceptor> aptr) { ...@@ -112,7 +112,8 @@ void publish_impl(abstract_actor_ptr ptr, std::unique_ptr<acceptor> aptr) {
abstract_actor_ptr remote_actor_impl(stream_ptr_pair io, string_set expected) { abstract_actor_ptr remote_actor_impl(stream_ptr_pair io, string_set expected) {
CPPA_LOGF_TRACE("io{" << io.first.get() << ", " << io.second.get() << "}"); CPPA_LOGF_TRACE("io{" << io.first.get() << ", " << io.second.get() << "}");
auto pinf = node_id::get(); auto mm = get_middleman();
auto pinf = mm->node();
std::uint32_t process_id = pinf->process_id(); std::uint32_t process_id = pinf->process_id();
// throws on error // throws on error
io.second->write(&process_id, sizeof(std::uint32_t)); io.second->write(&process_id, sizeof(std::uint32_t));
...@@ -196,7 +197,6 @@ abstract_actor_ptr remote_actor_impl(stream_ptr_pair io, string_set expected) { ...@@ -196,7 +197,6 @@ abstract_actor_ptr remote_actor_impl(stream_ptr_pair io, string_set expected) {
auto ptr = get_actor_registry()->get(remote_aid); auto ptr = get_actor_registry()->get(remote_aid);
return ptr; return ptr;
} }
auto mm = get_middleman();
struct remote_actor_result { remote_actor_result* next; actor value; }; struct remote_actor_result { remote_actor_result* next; actor value; };
intrusive::blocking_single_reader_queue<remote_actor_result> q; intrusive::blocking_single_reader_queue<remote_actor_result> q;
mm->run_later([mm, io, pinfptr, remote_aid, &q] { mm->run_later([mm, io, pinfptr, remote_aid, &q] {
......
...@@ -171,7 +171,7 @@ int main() { ...@@ -171,7 +171,7 @@ int main() {
actor_namespace addressing; actor_namespace addressing;
cout << "process id: " << to_string(node_id::get()) << endl; cout << "process id: " << to_string(get_middleman()->node()) << endl;
auto oarr = new detail::object_array; auto oarr = new detail::object_array;
oarr->push_back(object::from(static_cast<uint32_t>(42))); oarr->push_back(object::from(static_cast<uint32_t>(42)));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment