Compare commits

..

10 Commits

Author SHA1 Message Date
8ee44dceee Починил сборку под ARMv7 2015-01-05 21:47:37 +03:00
3f65576372 Отключил сборку ассемблерного кода (он не реализован для 32 битных
систем). Починил сборку с помощью clang на linux.
2015-01-01 16:12:58 +03:00
ac5aa179e6 session fix 2014-12-28 22:53:24 +03:00
abbd6b72d5 Добавил возможность останавливать сервер 2014-12-28 22:48:09 +03:00
Selim Mustafaev
d360fa0252 Небольшая чистка кода 2014-11-17 23:57:03 +03:00
Selim Mustafaev
99f137ca25 Перевел пример простого сервера на stackful coroutines (пока без обработки ошибок и завершения соединения) 2014-11-17 23:44:42 +03:00
Selim Mustafaev
efe4b8f50f добавил пример обработки данных, считанных из сокета, в пуле потоков и запись результата обратно в сокет (и все это в псевдолинейном стиле) 2014-11-08 22:21:13 +03:00
dcab8d1230 Добавил забытый до этого тест 2014-11-07 23:24:29 +03:00
5b3805e61c Адаптировал ассемблерный код для OS X. Добавил простой эхо сервер на boost.asio 2014-11-07 23:09:54 +03:00
28f2e968d8 Добавлена базовая поддержка ассемблера (NASM), пока проверено только на linux. ассеьблерный код переделан с FASM на NASM 2014-09-21 21:37:07 +04:00
15 changed files with 371 additions and 26 deletions

View File

@ -2,9 +2,10 @@ cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE DEBUG)
set(CMAKE_CXX_FLAGS "-std=c++11")
set(CMAKE_CXX_FLAGS "-std=c++11 -pthread -fsigned-char")
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
set(CMAKE_EXE_LINKER_FLAGS "-pthread")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib)

View File

@ -16,14 +16,18 @@ private:
std::queue<std::function<void()>> _queue;
std::mutex _mutex;
std::condition_variable _cv;
std::condition_variable _wait_cv;
std::vector<std::thread> _workers;
bool _enabled;
std::atomic<std::size_t> _tasksCount;
public:
threadpool();
threadpool(std::size_t concurrency);
~threadpool();
void wait_all();
template <typename R> std::future<R> add_task(std::function<R()> func)
{
std::lock_guard<std::mutex> lock(_mutex);
@ -31,6 +35,7 @@ public:
std::future<R> fut = pt_ptr->get_future();
_queue.push([pt_ptr] { (*pt_ptr)(); });
_tasksCount++;
_cv.notify_one();
return fut;
}

View File

@ -3,9 +3,24 @@ cmake_minimum_required(VERSION 2.8)
set(MAIN_PROJECT_NAME "cpputil")
set(SRC_DIR ".")
set(INCLUDE_DIR ../include ./asm/include)
set(ARCH "x86_64")
project(${MAIN_PROJECT_NAME} CXX)
if(APPLE)
set(SYSTEM "darwin")
else()
set(SYSTEM "linux")
endif()
SET(CMAKE_ASM_NASM_COMPILER yasm)
project(${MAIN_PROJECT_NAME} CXX ASM_NASM)
aux_source_directory(${SRC_DIR} SRC_FILES)
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
file(GLOB_RECURSE ASM_SRC_FILES "${SRC_DIR}/asm/${SYSTEM}/${ARCH}" "*.asm")
set(SRC_FILES ${SRC_FILES} ${ASM_SRC_FILES})
endif()
include_directories(${INCLUDE_DIR})
add_library(${MAIN_PROJECT_NAME} STATIC ${SRC_FILES})
target_link_libraries(${MAIN_PROJECT_NAME})

View File

@ -0,0 +1,101 @@
;
; public exported functions
;
global __asm_ucs4_strstr
global __asm_ucs4_strchr
;
; code section
;
section .text
; constants
npos db -1
;================================================================================
;
; Поиск подстроки в строке UCS-4 (алгоритм "в лоб", подойдет для коротких строк)
;
; rdi: указатель на исходную строку
; rsi: длина исходной строки символах
; rdx: указатель на искомую подстроку
; rcx: длина подстроки в символах
;
;================================================================================
__asm_ucs4_strstr:
push r12
mov r9d, dword [rdx]
mov rax, rdx
lea r8, [rdi + rsi*4]
lea r10, [rdx + rcx*4]
strstr_try_next_symbol:
cmp r9d, dword [rdi]
je strstr_init_counter
add rdi, 4
cmp rdi, r8
jne strstr_try_next_symbol
pop r12
mov rax, 0
ret
strstr_init_counter:
mov rdx, rax
mov r12, rdi
strstr_symbol_found:
add rdx, 4
cmp rdx, r10
je strstr_the_end
add r12, 4
mov r11d, dword [r12]
cmp r11d, dword [rdx]
je strstr_symbol_found
add rdi, 4
jmp strstr_try_next_symbol
strstr_the_end:
pop r12
mov rax, rdi
ret
;================================================================================
;
; Поиск символа в строке UCS-4
;
; rdi: указатель на исходную строку
; rsi: длина исходной строки символах
; rdx: искомый символ
;
;================================================================================
__asm_ucs4_strchr:
xor r8, r8
try_next_symbol:
cmp rdx, [rdi]
je symbol_found
add rdi, 4
inc r8
cmp rsi, r8
jne try_next_symbol
mov rax, 0
ret
symbol_found:
mov rax, rdi
ret

View File

@ -1,21 +1,19 @@
format ELF64
;
; public exported functions
;
public _asm_ucs4_strstr
public _asm_ucs4_strchr
global _asm_ucs4_strstr
global _asm_ucs4_strchr
;
; code section
;
section '.text' executable
section '.text'
; constants
npos = -1
npos db -1
;================================================================================
;

View File

@ -1,10 +1,11 @@
#include <iostream>
#include "threadpool.h"
threadpool::threadpool() : threadpool(std::thread::hardware_concurrency())
{
}
threadpool::threadpool(std::size_t concurrency) : _concurrency(concurrency), _enabled(true)
threadpool::threadpool(std::size_t concurrency) : _concurrency(concurrency), _enabled(true), _tasksCount(0)
{
for (std::size_t i = 0; i < _concurrency; ++i)
{
@ -38,5 +39,13 @@ void threadpool::worker_func()
lock.unlock();
func();
_tasksCount--;
_wait_cv.notify_one();
}
}
void threadpool::wait_all()
{
std::unique_lock<std::mutex> lock(_mutex);
_wait_cv.wait(lock, [this] { return _tasksCount == 0; });
}

View File

@ -381,19 +381,19 @@ std::size_t ustring::find(const ustring& str, std::size_t pos /* = 0 */) const
return npos;
}
#if defined(__GNUC__) && !defined(__clang__)
/*
#if defined(__GNUC__) // GCC and clang
char32_t* fpos = reinterpret_cast<char32_t*>(memmem(_pData + pos, (_len - pos)*sizeof(char32_t), str._pData, str._len*sizeof(char32_t)));
if(fpos)
{
return (fpos - _pData);
}
*/
/*
const char32_t* fpos = _asm_ucs4_strstr(_pData + pos, (_len - pos), str._pData, str._len);
if(fpos)
{
return (fpos - _pData);
}
*/
#else
const char32_t* pStr = str._pData;
const std::size_t len = _len - str.length();

View File

@ -1,12 +1,9 @@
#include "utf.h"
#include <cstring>
#if defined(__clang__)
#include <libkern/OSByteOrder.h>
#define bswap_32 OSSwapInt32
#define bswap_16 OSSwapInt16
#elif defined(__GNUC__)
#include <byteswap.h>
#if defined(__GNUC__)
#define bswap_16 __builtin_bswap16
#define bswap_32 __builtin_bswap32
#elif defined(_MSC_VER)
#define bswap_16 _byteswap_ushort
#define bswap_32 _byteswap_ulong

View File

@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 2.8)
set(TEST_PROJECT_NAME "cpputil_test")
set(TEST_SERVER_PROJECT_NAME "server_test")
set(TEST_CLIENT_PROJECT_NAME "client_test")
set(SRC_DIR ".")
set(REQUIRED_LIBRARIES cpputil)
set(boost_version 1.56.0)
@ -15,7 +17,7 @@ ExternalProject_Add(
TIMEOUT 100
CMAKE_ARGS -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=${CMAKE_SOURCE_DIR}/lib
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=${CMAKE_SOURCE_DIR}/lib
-Dgtest_force_shared_crt=ON
#-Dgtest_force_shared_crt=ON
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ""
@ -36,7 +38,7 @@ endif()
ExternalProject_Add(
boost
URL http://switch.dl.sourceforge.net/project/boost/boost/${boost_version}/boost_${boost_version_underscored}.zip
CONFIGURE_COMMAND ./bootstrap.sh --with-libraries=coroutine
CONFIGURE_COMMAND ./bootstrap.sh --with-libraries=coroutine,thread
BUILD_COMMAND ./b2 --ignore-site-config --stagedir=${CMAKE_SOURCE_DIR} link=static threading=multi cxxflags=-std=c++11 ${boost_build_type}
BUILD_IN_SOURCE 1
INSTALL_COMMAND ""
@ -46,17 +48,21 @@ ExternalProject_Add(
)
ExternalProject_Get_Property( boost source_dir )
ExternalProject_Get_Property( boost binary_dir )
set( Boost_INCLUDE_DIRS ${source_dir} )
set( Boost_LIBRARIES debug;${binary_dir}/stage/lib/libboost_program_options-vc110-mt-gd-1_49.lib;optimized;${binary_dir}/stage/lib/libboost_program_options-vc110-mt-1_49.lib )
set(BOOST_INCLUDE_DIR ${source_dir})
INCLUDE_DIRECTORIES(${BOOST_INCLUDE_DIR})
project(${TEST_PROJECT_NAME} CXX)
aux_source_directory(${SRC_DIR} TEST_SRC)
aux_source_directory(${SRC_DIR}/cpputil TEST_SRC)
add_executable(${TEST_PROJECT_NAME} ${TEST_SRC})
SET_TARGET_PROPERTIES(${TEST_PROJECT_NAME} PROPERTIES ENABLE_EXPORTS TRUE)
ADD_DEPENDENCIES(${TEST_PROJECT_NAME} googletest cpputil boost)
ADD_DEPENDENCIES(${TEST_PROJECT_NAME} googletest cpputil)
target_link_libraries(${TEST_PROJECT_NAME} ${REQUIRED_LIBRARIES} ${CMAKE_SOURCE_DIR}/lib/libgtest.a)
target_link_libraries(${TEST_PROJECT_NAME} ${REQUIRED_LIBRARIES} ${CMAKE_SOURCE_DIR}/lib/libgtest.a ${CMAKE_SOURCE_DIR}/lib/libboost_coroutine.a)
project(${TEST_SERVER_PROJECT_NAME} CXX)
aux_source_directory(${SRC_DIR}/server TEST_SERVER_SRC)
add_executable(${TEST_SERVER_PROJECT_NAME} ${TEST_SERVER_SRC})
add_dependencies(${TEST_SERVER_PROJECT_NAME} boost cpputil)
target_link_libraries(${TEST_SERVER_PROJECT_NAME} cpputil ${CMAKE_SOURCE_DIR}/lib/libboost_system.a ${CMAKE_SOURCE_DIR}/lib/libboost_coroutine.a ${CMAKE_SOURCE_DIR}/lib/libboost_context.a ${CMAKE_SOURCE_DIR}/lib/libboost_thread.a)
install(TARGETS ${TEST_PROJECT_NAME}
RUNTIME DESTINATION bin

34
test/server/main.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "session.h"
#include "server.h"
int main()
{
server srv("0.0.0.0", "12345");
std::thread t([&]{
std::this_thread::sleep_for(std::chrono::seconds(10));
srv.stop();
});
t.detach();
srv.run();
std::cout << "after run" << std::endl;
/*
threadpool pool;
pool.add_task<void>([]{
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "first task completed" << std::endl;
});
pool.add_task<void>([]{
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "second task completed" << std::endl;
});
pool.wait_all();
std::cout << "all tasks completed" << std::endl;
*/
return 0;
}

50
test/server/server.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "server.h"
#include <boost/asio/spawn.hpp>
server::server(std::string address, std::string port)
{
tcp::resolver resolver(_io_service);
tcp::resolver::query query(address, port);
_acceptor = new tcp::acceptor(_io_service, *resolver.resolve(query));
}
void server::run()
{
boost::asio::spawn(_io_service, [&](boost::asio::yield_context yield) {
try
{
for (;;)
{
session *s = new session(_io_service, &_pool);
_acceptor->async_accept(s->socket(), yield);
_sessions.insert(s);
s->start();
}
}
catch (boost::system::system_error& ex)
{
std::cout << "server exception: " << ex.what() << std::endl;
}
});
_io_service.run();
std::cout << "after ioservice run" << std::endl;
std::for_each(_sessions.begin(), _sessions.end(), [](session* s){ delete s; });
_sessions.clear();
}
server::~server()
{
std::cout << "server destructor" << std::endl;
delete _acceptor;
}
void server::stop()
{
_io_service.post([&]{
_acceptor->close();
std::for_each(_sessions.begin(), _sessions.end(), [](session* s){ s->stop(); });
_pool.wait_all();
std::cout << "stop exited" << std::endl;
});
}

30
test/server/server.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef _SERVER_H_
#define _SERVER_H_
#include "threadpool.h"
#include "session.h"
#include <boost/asio.hpp>
#include <set>
class server
{
private:
typedef boost::asio::ip::tcp tcp;
private:
boost::asio::io_service _io_service;
tcp::acceptor* _acceptor;
threadpool _pool;
std::set<session*> _sessions;
public:
server() = delete;
server(server&) = delete;
server(std::string address, std::string port);
void run();
void stop();
~server();
};
#endif // _SERVER_H_

58
test/server/session.cpp Normal file
View File

@ -0,0 +1,58 @@
#include "session.h"
#include <iostream>
#include <boost/asio/spawn.hpp>
#include <thread>
session::session(boost::asio::io_service &io_service, threadpool* pool): _socket(io_service), _pool(pool)
{
}
void session::start()
{
boost::asio::spawn(_socket.get_io_service(), [this](boost::asio::yield_context yield){
try
{
for (;;) {
std::size_t bytes = _socket.async_read_some(boost::asio::buffer(_data, 128), yield);
std::cout << "before async call" << std::endl;
int n = async_call<int>([this] {
std::cout << "async call start waiting" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(15));
std::cout << "async call ends waiting" << std::endl;
int n = std::atoi(_data);
return n * n;
}, yield);
std::cout << "before write: " << n << std::endl;
_socket.async_write_some(boost::asio::buffer(std::to_string(n) + "\n"), yield);
std::cout << "after write" << std::endl;
}
}
catch (boost::system::system_error& ex)
{
std::cout << "exception: " << ex.code().value() << " - " << ex.what() << std::endl;
if(ex.code().value() == 2) // end of file
{
//FIXME: Удалять сессию должен сервер
delete this;
}
}
});
}
boost::asio::ip::tcp::socket& session::socket()
{
return _socket;
}
void session::stop()
{
_socket.close();
}
session::~session()
{
std::cout << "session destructor called" << std::endl;
}

41
test/server/session.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef _SESSION_H_
#define _SESSION_H_
#include "threadpool.h"
#include <boost/asio.hpp>
#include <iostream>
class session
{
private:
typedef boost::asio::ip::tcp tcp;
private:
tcp::socket _socket;
char _data[128];
threadpool* _pool;
public:
session() = delete;
session(session&) = delete;
session(boost::asio::io_service& io_service, threadpool* pool);
~session();
void start();
void stop();
tcp::socket& socket();
template <typename R, typename CompletionToken> R async_call(std::function<R()> func, CompletionToken&& token)
{
using namespace boost::asio;
BOOST_ASIO_HANDLER_TYPE(CompletionToken, void(R)) handler(std::forward<CompletionToken>(token));
async_result<decltype(handler)> result(handler);
_pool->add_task<void>([this, handler, &func]{
_socket.get_io_service().dispatch(std::bind(handler, func()));
});
return result.get();
}
};
#endif // _SESSION_H_