├── .gitattributes ├── .gitignore ├── CHANGELOG ├── CMakeLists.txt ├── CppAwait ├── AsioWrappers.cpp ├── Awaitable.cpp ├── CMakeLists.txt ├── ConfigPrivate.h ├── Coro.cpp ├── Log.cpp ├── Scheduler.cpp ├── SharedFlag.cpp ├── StringUtil.cpp ├── stdafx.cpp └── stdafx.h ├── Examples ├── CMakeLists.txt ├── ExUtil.h ├── Looper │ ├── Chrono.cpp │ ├── Chrono.h │ ├── Looper.cpp │ ├── Looper.h │ └── Thread.h ├── ex_awaitBasics.cpp ├── ex_awaitChatClient.cpp ├── ex_awaitChatServer.cpp ├── ex_awaitFlickr.cpp ├── ex_awaitHttpClient.cpp ├── ex_awaitThread.cpp ├── ex_comboDetector.cpp ├── ex_fibonacci.cpp ├── ex_iterator.cpp ├── ex_stockClient.cpp ├── ex_stockServer.cpp ├── main.cpp ├── stdafx.cpp └── stdafx.h ├── LICENSE ├── PROJECTS ├── README.md ├── TODO ├── cmake_modules ├── enable_max_warning_level.cmake └── precompiled_header.cmake ├── doc └── Doxyfile └── include └── CppAwait ├── AsioWrappers.h ├── Awaitable.h ├── BoundedQueue.h ├── Condition.h ├── Config.h ├── Coro.h ├── Log.h ├── YieldSequence.h ├── impl ├── Assert.h ├── Compatibility.h ├── Foreach.h ├── SharedFlag.h └── StringUtil.h └── misc ├── FastAction.h ├── Functional.h ├── HybridVector.h ├── OpaqueSharedPtr.h ├── Scheduler.h ├── ScopeGuard.h └── Signals.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behaviour, in case users don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files we want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.c text 7 | *.cmake text 8 | *.cpp text 9 | *.h text 10 | *.md text 11 | *.txt text 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bat 2 | *.suo 3 | *.kdev* 4 | .project 5 | .cproject 6 | .settings 7 | CMakeLists.txt.user 8 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | - wip -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | 3 | project (CppAwaitSolution CXX) 4 | 5 | # 6 | # modules 7 | # 8 | 9 | set (CMAKE_MODULE_PATH ${CppAwaitSolution_SOURCE_DIR}/cmake_modules ${CMAKE_MODULE_PATH}) 10 | 11 | include (precompiled_header) 12 | include (enable_max_warning_level) 13 | 14 | # 15 | # Boost 16 | # 17 | 18 | set (Boost_USE_STATIC_RUNTIME OFF) 19 | set (Boost_USE_STATIC_LIBS ON) 20 | 21 | if (MSVC10 OR MINGW) 22 | # MSVC10 & regular MINGW don't provide chrono & thread, fallback to Boost 23 | set (_boost_deps chrono context thread system) 24 | 25 | # mingw-gcc fails to link boost::thread 26 | if (WIN32 AND CMAKE_COMPILER_IS_GNUCXX) 27 | add_definitions (-DBOOST_THREAD_USE_LIB) 28 | endif() 29 | else() 30 | set (_boost_deps context thread system) 31 | endif() 32 | 33 | message ("Boost dependencies: ${_boost_deps}") 34 | 35 | find_package (Boost 1.58.0 REQUIRED COMPONENTS ${_boost_deps}) 36 | 37 | message ("Using Boost ${Boost_LIB_VERSION} from ${Boost_INCLUDE_DIRS}") 38 | include_directories (${Boost_INCLUDE_DIRS}) 39 | 40 | # disable Boost autolinking 41 | add_definitions (-DBOOST_ALL_NO_LIB) 42 | 43 | # 44 | # OpenSSL 45 | # 46 | 47 | find_package (OpenSSL) 48 | 49 | if (OPENSSL_FOUND) 50 | message ("Using OpenSSL ${OPENSSL_VERSION} from ${OPENSSL_INCLUDE_DIR}") 51 | include_directories (${OPENSSL_INCLUDE_DIR}) 52 | 53 | # enable Flickr example 54 | add_definitions (-DHAVE_OPENSSL) 55 | endif() 56 | 57 | # 58 | # defs 59 | # 60 | 61 | if (CMAKE_COMPILER_IS_GNUCXX) 62 | add_definitions (--std=gnu++0x -Wno-unused-parameter) 63 | elseif (MSVC) 64 | add_definitions (/wd4100 /wd4127 /wd4913) 65 | endif() 66 | 67 | # 68 | # paths 69 | # 70 | 71 | set (CMAKE_INCLUDE_CURRENT_DIR TRUE) 72 | 73 | include_directories (include) 74 | 75 | add_subdirectory (CppAwait) 76 | add_subdirectory (Examples) 77 | -------------------------------------------------------------------------------- /CppAwait/AsioWrappers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ConfigPrivate.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace ut { namespace asio { 25 | 26 | using namespace boost::asio; 27 | using namespace boost::asio::ip; 28 | 29 | template 30 | static void doAsyncHttpGet(Socket& socket, 31 | const std::string& host, const std::string& path, bool persistentConnection, 32 | bool readAll, std::shared_ptr outResponse, size_t& outContentLength) 33 | { 34 | if (!socket.lowest_layer().is_open()) { 35 | throw std::runtime_error("socket not connected"); 36 | } 37 | 38 | auto request = std::make_shared(); 39 | 40 | // write HTTP request 41 | std::ostream requestStream(request.get()); 42 | requestStream << "GET " << path << " HTTP/1.1\r\n"; 43 | requestStream << "Host: " << host << "\r\n"; 44 | requestStream << "Accept: */*\r\n"; 45 | if (!persistentConnection) { 46 | requestStream << "Connection: close\r\n"; 47 | } 48 | requestStream << "\r\n"; 49 | 50 | Awaitable awt = asyncWrite(socket, request); 51 | awt.await(); 52 | 53 | // read first response line 54 | awt = asyncReadUntil(socket, outResponse, std::string("\r\n")); 55 | awt.await(); 56 | 57 | std::istream responseStream(outResponse.get()); 58 | std::string httpVersion; 59 | responseStream >> httpVersion; 60 | int statusCode; 61 | responseStream >> statusCode; 62 | std::string statusMessage; 63 | std::getline(responseStream, statusMessage); 64 | 65 | if (!responseStream || !boost::starts_with(httpVersion, "HTTP/")) { 66 | throw std::runtime_error("invalid HTTP response"); 67 | } 68 | if (statusCode != 200) { 69 | throw std::runtime_error(string_printf("bad HTTP status: %d", statusCode)); 70 | } 71 | 72 | // read response headers 73 | awt = asyncReadUntil(socket, outResponse, std::string("\r\n\r\n")); 74 | awt.await(); 75 | 76 | // process headers 77 | std::string header; 78 | outContentLength = (size_t) -1; 79 | 80 | while (std::getline(responseStream, header) && header != "\r") { 81 | if (boost::starts_with(header, "Content-Length: ")) { 82 | auto l = header.substr(strlen("Content-Length: ")); 83 | l.resize(l.size() - 1); 84 | outContentLength = boost::lexical_cast(l); 85 | } 86 | } 87 | 88 | if (readAll) { 89 | size_t numBytesRemaining = outContentLength - outResponse->size(); 90 | size_t numBytesTransferred; 91 | awt = asyncRead(socket, outResponse, asio::transfer_exactly(numBytesRemaining), numBytesTransferred); 92 | awt.await(); 93 | } 94 | } 95 | 96 | namespace detail { 97 | 98 | void doAsyncHttpGet(boost::asio::ip::tcp::socket& socket, 99 | const std::string& host, const std::string& path, bool persistentConnection, 100 | bool readAll, std::shared_ptr outResponse, size_t& outContentLength) 101 | { 102 | ut::asio::doAsyncHttpGet(socket, host, path, persistentConnection, readAll, outResponse, outContentLength); 103 | } 104 | 105 | #ifdef HAVE_OPENSSL 106 | void doAsyncHttpGet(boost::asio::ssl::stream& socket, 107 | const std::string& host, const std::string& path, bool persistentConnection, 108 | bool readAll, std::shared_ptr outResponse, size_t& outContentLength) 109 | { 110 | ut::asio::doAsyncHttpGet(socket, host, path, persistentConnection, readAll, outResponse, outContentLength); 111 | } 112 | #endif 113 | } 114 | 115 | Awaitable asyncHttpDownload(boost::asio::io_service& io, 116 | const std::string& host, const std::string& path, 117 | std::shared_ptr outResponse) 118 | { 119 | static int id = 0; 120 | auto tag = string_printf("asyncHttpDownload-%d", id++); 121 | 122 | return startAsync(std::move(tag), [&io, host, path, outResponse]() { 123 | tcp::socket socket(io); 124 | 125 | tcp::resolver::query query(host, "http"); 126 | tcp::resolver::iterator itConnected; 127 | Awaitable awt = asyncResolveAndConnect(socket, query, itConnected); 128 | awt.await(); 129 | 130 | size_t contentLength; 131 | detail::doAsyncHttpGet(socket, host, path, false, true, outResponse, contentLength); 132 | }); 133 | } 134 | 135 | #ifdef HAVE_OPENSSL 136 | 137 | Awaitable asyncHttpsDownload(boost::asio::io_service& io, 138 | ssl::context_base::method sslVersion, 139 | const std::string& host, const std::string& path, 140 | std::shared_ptr outResponse) 141 | { 142 | static int id = 0; 143 | auto tag = string_printf("asyncHttpsDownload-%d", id++); 144 | 145 | return startAsync(std::move(tag), [&io, sslVersion, host, path, outResponse]() { 146 | // prepare SSL client socket 147 | typedef ssl::stream ssl_socket; 148 | ssl::context ctx(sslVersion); 149 | ssl_socket socket(io, ctx); 150 | 151 | tcp::resolver::query query(host, "https"); 152 | tcp::resolver::iterator itConnected; 153 | Awaitable awt = asyncResolveAndConnect(socket.lowest_layer(), query, itConnected); 154 | awt.await(); 155 | 156 | socket.lowest_layer().set_option(tcp::no_delay(true)); 157 | 158 | // perform SSL handshake 159 | awt = asyncHandshake(socket, ssl_socket::client); 160 | awt.await(); 161 | 162 | size_t contentLength; 163 | detail::doAsyncHttpGet(socket, host, path, false, true, outResponse, contentLength); 164 | }); 165 | } 166 | 167 | #endif // HAVE_OPENSSL 168 | 169 | } } 170 | -------------------------------------------------------------------------------- /CppAwait/Awaitable.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ConfigPrivate.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace ut { 28 | 29 | // 30 | // Awaitable 31 | // 32 | 33 | struct AwaitableImpl 34 | { 35 | Awaitable *shell; 36 | std::string tag; 37 | Coro *boundCoro; 38 | Coro *awaitingCoro; 39 | bool didComplete; 40 | std::exception_ptr exceptionPtr; 41 | std::shared_ptr completerGuard; 42 | Signal0 onDone; 43 | 44 | AwaitableImpl(std::string&& tag) 45 | : shell(nullptr) 46 | , tag(std::move(tag)) 47 | , boundCoro(nullptr) 48 | , awaitingCoro(nullptr) 49 | , didComplete(false) 50 | { 51 | } 52 | }; 53 | 54 | typedef boost::singleton_pool< 55 | AwaitableImpl, sizeof(AwaitableImpl), 56 | boost::default_user_allocator_new_delete, 57 | boost::details::pool::null_mutex> AwtImplPool; 58 | 59 | Awaitable::Awaitable(std::string tag) 60 | { 61 | currentCoro(); // ensure library is initialized 62 | 63 | m = (AwaitableImpl *) AwtImplPool::malloc(); 64 | new (m) AwaitableImpl(std::move(tag)); 65 | m->shell = this; 66 | } 67 | 68 | Awaitable::~Awaitable() 69 | { 70 | clear(); 71 | } 72 | 73 | Awaitable::Awaitable(Awaitable&& other) 74 | { 75 | m = other.m; 76 | other.m = nullptr; 77 | m->shell = this; 78 | } 79 | 80 | Awaitable& Awaitable::operator=(Awaitable&& other) 81 | { 82 | clear(); 83 | m = other.m; 84 | other.m = nullptr; 85 | 86 | if (m) { 87 | m->shell = this; 88 | } 89 | 90 | return *this; 91 | } 92 | 93 | void Awaitable::await() 94 | { 95 | ut_assert_(m->awaitingCoro == nullptr && "already being awaited"); 96 | 97 | if (m->didComplete) { 98 | ut_log_debug_("* await '%s' from '%s' (done)", tag(), currentCoro()->tag()); 99 | } else if (is(m->exceptionPtr)) { 100 | ut_log_debug_("* await '%s' from '%s' (done - exception)", tag(), currentCoro()->tag()); 101 | 102 | std::rethrow_exception(m->exceptionPtr); 103 | } else { 104 | ut_log_debug_("* await '%s' from '%s'", tag(), currentCoro()->tag()); 105 | 106 | ut_assert_(!isNil() && "completer not taken"); 107 | ut_assert_(currentCoro() != masterCoro() && "awaiting would suspend master coro"); 108 | 109 | m->awaitingCoro = currentCoro(); 110 | yieldTo(masterCoro()); 111 | 112 | ut_assert_(isDone()); 113 | m->awaitingCoro = nullptr; 114 | 115 | if (is(m->exceptionPtr)) { 116 | std::rethrow_exception(m->exceptionPtr); 117 | } 118 | } 119 | } 120 | 121 | bool Awaitable::didComplete() 122 | { 123 | return m->didComplete; 124 | } 125 | 126 | bool Awaitable::didFail() 127 | { 128 | return is(m->exceptionPtr); 129 | } 130 | 131 | bool Awaitable::isDone() 132 | { 133 | return didComplete() || didFail(); 134 | } 135 | 136 | std::exception_ptr Awaitable::exception() 137 | { 138 | return m->exceptionPtr; 139 | } 140 | 141 | void Awaitable::then(ut::Action slot) 142 | { 143 | return m->onDone.connectLite(std::move(slot)); 144 | } 145 | 146 | Completer Awaitable::takeCompleter() 147 | { 148 | ut_log_info_("* new evt-awt '%s'", m->tag.c_str()); 149 | 150 | ut_assert_(isNil() && "completer already taken"); 151 | 152 | m->completerGuard = allocateSharedFlag(m); 153 | 154 | return Completer(m->completerGuard); 155 | } 156 | 157 | bool Awaitable::isNil() 158 | { 159 | return !isDone() && !m->completerGuard; 160 | } 161 | 162 | const char* Awaitable::tag() 163 | { 164 | return m->tag.c_str(); 165 | } 166 | 167 | void Awaitable::setTag(std::string tag) 168 | { 169 | m->tag = std::move(tag); 170 | } 171 | 172 | Awaitable::Pointer Awaitable::pointer() 173 | { 174 | return Pointer(m); 175 | } 176 | 177 | void Awaitable::setAwaitingCoro(Coro *coro) 178 | { 179 | ut_assert_(!isNil() && "completer not taken"); 180 | 181 | m->awaitingCoro = coro; 182 | } 183 | 184 | void Awaitable::complete() 185 | { 186 | ut_assert_(!didComplete()); 187 | ut_assert_(!didFail()); 188 | 189 | m->didComplete = true; 190 | m->completerGuard.reset(); 191 | 192 | if (m->awaitingCoro == nullptr) { 193 | m->onDone(); 194 | } else { 195 | m->onDone(); 196 | 197 | if (currentCoro() != masterCoro() && currentCoro() != m->boundCoro) { 198 | ut_assert_(false && "called from wrong coroutine"); 199 | } 200 | 201 | yieldTo(m->awaitingCoro, this); 202 | } 203 | } 204 | 205 | void Awaitable::fail(std::exception_ptr eptr) 206 | { 207 | ut_assert_(!didComplete()); 208 | ut_assert_(!didFail()); 209 | 210 | ut_assert_(is(eptr) && "invalid exception_ptr"); 211 | 212 | m->exceptionPtr = std::move(eptr); 213 | m->completerGuard.reset(); 214 | 215 | if (m->awaitingCoro == nullptr) { 216 | m->onDone(); 217 | } else { 218 | m->onDone(); 219 | 220 | if (currentCoro() != masterCoro() && currentCoro() != m->boundCoro) { 221 | ut_assert_(false && "called from wrong coroutine"); 222 | } 223 | 224 | yieldTo(m->awaitingCoro, this); 225 | } 226 | } 227 | 228 | void Awaitable::clear() 229 | { 230 | if (!m) { 231 | return; // moved 232 | } 233 | 234 | if (didComplete() || didFail()) { // is done 235 | ut_log_debug_("* destroy awt '%s' %s(%s)", tag(), 236 | (std::uncaught_exception() ? "due to uncaught exception " : ""), 237 | (didComplete() ? "completed" : "failed")); 238 | 239 | ut_assert_(m->awaitingCoro == nullptr); 240 | } else if (m->completerGuard) { // not nil 241 | ut_log_debug_("* destroy awt '%s' %s(interrupted)", tag(), 242 | (std::uncaught_exception() ? "due to uncaught exception " : "")); 243 | 244 | if (m->awaitingCoro != nullptr) { 245 | // can't print awaiting coroutine tag since it may have been deleted 246 | // (e.g. a persistent Awaitable may outlive its awaiter) 247 | ut_log_info_("* while being awaited"); 248 | m->awaitingCoro = nullptr; 249 | } 250 | 251 | if (m->boundCoro != nullptr) { 252 | ut_log_debug_("* force bound coroutine '%s' to unwind", m->boundCoro->tag()); 253 | 254 | ut_assert_(m->boundCoro->isRunning()); 255 | 256 | { PushMasterCoro _; // take over 257 | // resume coroutine, force fail() via ForcedUnwind 258 | forceUnwind(m->boundCoro); 259 | } 260 | 261 | ut_log_debug_("* unwinded '%s' of awt '%s'", m->boundCoro->tag(), tag()); 262 | } else { 263 | ut_log_info_("* fail awt '%s'", tag()); 264 | 265 | fail(YieldForbidden::ptr()); 266 | } 267 | } 268 | 269 | if (m->boundCoro != nullptr) { 270 | ut_assert_(!m->boundCoro->isRunning()); 271 | delete m->boundCoro; 272 | } 273 | 274 | m->~AwaitableImpl(); 275 | AwtImplPool::free(m); 276 | m = nullptr; 277 | } 278 | 279 | Awaitable Awaitable::makeCompleted() 280 | { 281 | Awaitable awt; 282 | awt.complete(); 283 | return std::move(awt); 284 | } 285 | 286 | Awaitable Awaitable::makeFailed(std::exception_ptr eptr) 287 | { 288 | Awaitable awt; 289 | awt.fail(std::move(eptr)); 290 | return std::move(awt); 291 | } 292 | 293 | Awaitable startAsync(std::string tag, Action func, size_t stackSize) 294 | { 295 | ut_log_info_("* new coro-awt '%s'", tag.c_str()); 296 | 297 | Awaitable awt(tag); 298 | 299 | // coroutine owns completer 300 | awt.m->completerGuard = allocateSharedFlag(); 301 | 302 | auto coro = new Coro(std::move(tag), [func](void *awtImpl) { 303 | auto m = (AwaitableImpl *) awtImpl; 304 | std::exception_ptr eptr; 305 | 306 | try { 307 | func(); 308 | 309 | ut_log_info_("* complete coro-awt '%s'", m->shell->tag()); 310 | } catch (const ForcedUnwind&) { 311 | ut_log_info_("* fail coro-awt '%s' (forced unwind)", m->shell->tag()); 312 | 313 | // If an Awaitable is being destroyed during propagation of some exception, 314 | // and the Awaitable is not yet done, it will interrupt itself via ForcedUnwind. 315 | // In this case, std::current_exception is unreliable: on MSVC it will return 316 | // empty inside the catch block of the inner exception. As workaround we use 317 | // a premade exception_ptr. 318 | 319 | eptr = ForcedUnwind::ptr(); 320 | } catch (...) { 321 | ut_log_info_("* fail coro-awt '%s' (exception)", m->shell->tag()); 322 | 323 | ut_assert_(!std::uncaught_exception() && "may not throw from async coroutine while another exception is propagating"); 324 | 325 | eptr = std::current_exception(); 326 | ut_assert_(is(eptr)); 327 | } 328 | 329 | ut_assert_(!m->shell->didFail()); 330 | ut_assert_(!m->shell->didComplete()); 331 | 332 | if (m->awaitingCoro != nullptr) { 333 | // wait until coroutine fully unwinded before yielding to awaiter 334 | m->boundCoro->setParent(m->awaitingCoro); 335 | m->awaitingCoro = nullptr; 336 | } else { 337 | // wait until coroutine fully unwinded before yielding to master 338 | m->boundCoro->setParent(masterCoro()); 339 | } 340 | 341 | if (is(eptr)) { 342 | m->shell->fail(std::move(eptr)); // mAwaitingCoro is null, won't yield 343 | } else { 344 | m->shell->complete(); // mAwaitingCoro is null, won't yield 345 | } 346 | 347 | // This function will never throw an exception. Instead, exceptions 348 | // are stored in the Awaitable and get rethrown by await(). 349 | }, stackSize); 350 | 351 | awt.m->boundCoro = coro; 352 | 353 | { PushMasterCoro _; // take over 354 | // run coro until it awaits or finishes 355 | yieldTo(coro, awt.m); 356 | } 357 | 358 | return std::move(awt); 359 | } 360 | 361 | // 362 | // Pointer 363 | // 364 | 365 | Awaitable* Awaitable::Pointer::get() const 366 | { 367 | return m->shell; 368 | } 369 | 370 | // 371 | // Completer 372 | // 373 | 374 | Awaitable* Completer::awaitable() const 375 | { 376 | if (auto strongRef = mRef.lock()) { 377 | return ((AwaitableImpl *) *strongRef)->shell; 378 | } else { 379 | return nullptr; 380 | } 381 | } 382 | 383 | void Completer::complete() const 384 | { 385 | ut_assert_msg_(currentCoro() == masterCoro(), 386 | "can't complete from '%s' because '%s' is master coro", currentCoro()->tag(), masterCoro()->tag()); 387 | 388 | if (auto strongRef = mRef.lock()) { 389 | auto shell = ((AwaitableImpl *) *strongRef)->shell; 390 | 391 | ut_log_info_("* complete awt '%s'", shell->tag()); 392 | shell->complete(); 393 | } 394 | } 395 | 396 | void Completer::fail(std::exception_ptr eptr) const 397 | { 398 | ut_assert_msg_(currentCoro() == masterCoro(), 399 | "can't fail from '%s' because '%s' is master coro", currentCoro()->tag(), masterCoro()->tag()); 400 | 401 | if (auto strongRef = mRef.lock()) { 402 | auto shell = ((AwaitableImpl *) *strongRef)->shell; 403 | 404 | ut_log_info_("* fail awt '%s'", shell->tag()); 405 | shell->fail(std::move(eptr)); 406 | } 407 | } 408 | 409 | } 410 | -------------------------------------------------------------------------------- /CppAwait/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file (GLOB _src_cxx *.cpp) 2 | file (GLOB _src_h *.h) 3 | file (GLOB _public_h ../include/CppAwait/*.h) 4 | file (GLOB _public_misc_h ../include/CppAwait/misc/*.h) 5 | file (GLOB _public_impl_h ../include/CppAwait/impl/*.h) 6 | 7 | source_group ("Sources" FILES ${_src_cxx} ${_src_h}) 8 | source_group ("Include" FILES ${_public_h}) 9 | source_group ("Include\\misc" FILES ${_public_misc_h}) 10 | source_group ("Include\\impl" FILES ${_public_impl_h}) 11 | 12 | if (NOT MINGW) 13 | add_precompiled_header (stdafx.h _src_cxx) 14 | endif() 15 | 16 | add_library (cpp_await ${_src_cxx} ${_src_h} ${_public_h} ${_public_misc_h} ${_public_impl_h}) 17 | -------------------------------------------------------------------------------- /CppAwait/ConfigPrivate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | -------------------------------------------------------------------------------- /CppAwait/Log.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ConfigPrivate.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace ut { 25 | 26 | static const char* PREFIXES[] = { "", "[UT-WARN] ", "[UT-INFO] ", "[UT-DEBG] ", "[UT-VERB] " }; 27 | 28 | static const int PREFIX_LEN = 10; 29 | 30 | static const int LOG_BUF_SIZE = 1024; 31 | 32 | static char sBuffer[LOG_BUF_SIZE]; 33 | 34 | 35 | LogLevel gLogLevel = LOGLEVEL_WARN; 36 | 37 | 38 | void implLog(LogLevel logLevel, const char *format, ...) 39 | { 40 | ut_assert_(logLevel <= gLogLevel); 41 | 42 | va_list ap; 43 | va_start(ap, format); 44 | 45 | memcpy(sBuffer, PREFIXES[logLevel], PREFIX_LEN + 1); 46 | 47 | vsnprintf(sBuffer + PREFIX_LEN, LOG_BUF_SIZE - PREFIX_LEN, format, ap); 48 | 49 | printf("%s\n", sBuffer); 50 | 51 | va_end(ap); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /CppAwait/Scheduler.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ConfigPrivate.h" 18 | #include 19 | #include 20 | 21 | namespace ut { 22 | 23 | // runs an action unless it has been canceled 24 | // 25 | class WeakAction 26 | { 27 | public: 28 | WeakAction() { } 29 | 30 | WeakAction(const std::shared_ptr& func) 31 | : mAction(func) { } 32 | 33 | WeakAction(const WeakAction& other) 34 | : mAction(other.mAction) { } 35 | 36 | WeakAction& operator=(const WeakAction& other) 37 | { 38 | if (this != &other) { 39 | mAction = other.mAction; 40 | } 41 | 42 | return *this; 43 | } 44 | 45 | WeakAction(WeakAction&& other) 46 | : mAction(std::move(other.mAction)) { } 47 | 48 | WeakAction& operator=(WeakAction&& other) 49 | { 50 | mAction = std::move(other.mAction); 51 | return *this; 52 | } 53 | 54 | void operator()() const 55 | { 56 | if (std::shared_ptr action = mAction.lock()) { 57 | (*action)(); 58 | 59 | // reset functor, otherwise it leaks until Ticket reset 60 | (*action) = Action(); 61 | } 62 | } 63 | 64 | private: 65 | std::weak_ptr mAction; 66 | }; 67 | 68 | 69 | static ScheduleFunc sSchedule = nullptr; 70 | 71 | void initScheduler(ScheduleFunc schedule) 72 | { 73 | sSchedule = schedule; 74 | } 75 | 76 | void schedule(Action action) 77 | { 78 | ut_assert_(sSchedule != nullptr && "scheduler not initialized, call initScheduler()"); 79 | 80 | sSchedule(std::move(action)); 81 | } 82 | 83 | Ticket scheduleWithTicket(Action action) 84 | { 85 | ut_assert_(sSchedule != nullptr && "scheduler not initialized, call initScheduler()"); 86 | 87 | auto sharedAction = std::make_shared(std::move(action)); 88 | 89 | sSchedule(WeakAction(sharedAction)); 90 | 91 | return Ticket(std::move(sharedAction)); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /CppAwait/SharedFlag.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ConfigPrivate.h" 18 | #include 19 | 20 | #ifdef UT_ENABLE_SHARED_FLAG_POOL 21 | 22 | #include 23 | 24 | namespace ut { 25 | 26 | typedef boost::fast_pool_allocator< 27 | SharedFlag, 28 | boost::default_user_allocator_new_delete, 29 | boost::details::pool::default_mutex> SharedFlagAllocator; 30 | 31 | SharedFlag allocateSharedFlag(void *value) 32 | { 33 | return std::allocate_shared(SharedFlagAllocator(), value); 34 | } 35 | 36 | } 37 | 38 | #else 39 | 40 | namespace ut { 41 | 42 | SharedFlag allocateSharedFlag(void *value) 43 | { 44 | return std::make_shared(value); 45 | } 46 | 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /CppAwait/StringUtil.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ConfigPrivate.h" 18 | #include 19 | #include 20 | #include 21 | 22 | namespace ut { 23 | 24 | #ifdef _MSC_VER 25 | 26 | int c99_snprintf(char *outBuf, size_t size, const char *format, ...) 27 | { 28 | int count; 29 | va_list ap; 30 | 31 | va_start(ap, format); 32 | count = c99_vsnprintf(outBuf, size, format, ap); 33 | va_end(ap); 34 | 35 | return count; 36 | } 37 | 38 | int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) 39 | { 40 | int count = -1; 41 | 42 | if (size != 0) 43 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); 44 | if (count == -1) 45 | count = _vscprintf(format, ap); 46 | 47 | return count; 48 | } 49 | 50 | #endif 51 | 52 | int safe_vprintf(std::vector& outBuf, size_t pos, const char *format, va_list ap) 53 | { 54 | ut_assert_(pos <= outBuf.size()); 55 | 56 | va_list apCopy; 57 | va_copy(apCopy, ap); 58 | 59 | int numChars = vsnprintf(outBuf.data() + pos, outBuf.size() - pos, format, ap); 60 | 61 | if (numChars >= 0) { 62 | if (numChars >= (int) (outBuf.size() - pos)) { 63 | size_t newSize = pos + numChars + 1; 64 | 65 | if (newSize < outBuf.size() * 2 + 64) 66 | newSize = outBuf.size() * 2 + 64; 67 | 68 | outBuf.resize(newSize); 69 | vsnprintf(outBuf.data() + pos, outBuf.size() - pos, format, apCopy); 70 | } 71 | } 72 | 73 | return numChars; 74 | } 75 | 76 | int safe_printf(std::vector& outBuf, size_t pos, const char *format, ...) 77 | { 78 | va_list ap; 79 | va_start(ap, format); 80 | int numChars = safe_vprintf(outBuf, pos, format, ap); 81 | va_end(ap); 82 | return numChars; 83 | } 84 | 85 | std::string string_vprintf(const char *format, va_list ap) 86 | { 87 | std::vector buf; 88 | int numChars = safe_vprintf(buf, 0, format, ap); 89 | 90 | return (numChars < 0 ? "" : std::string(buf.data(), numChars)); 91 | } 92 | 93 | std::string string_printf(const char *format, ...) 94 | { 95 | va_list ap; 96 | va_start(ap, format); 97 | std::string s = string_vprintf(format, ap); 98 | va_end(ap); 99 | return s; 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /CppAwait/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" -------------------------------------------------------------------------------- /CppAwait/stdafx.h: -------------------------------------------------------------------------------- 1 | #include "ConfigPrivate.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | -------------------------------------------------------------------------------- /Examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file (GLOB _src_cxx *.cpp) 2 | file (GLOB _src_h *.h) 3 | file (GLOB _src_looper_cxx Looper/*.cpp) 4 | file (GLOB _src_looper_h Looper/*.h) 5 | 6 | source_group ("Sources" FILES ${_src_cxx} ${_src_h}) 7 | source_group ("Sources\\Looper" FILES ${_src_looper_cxx} ${_src_looper_h}) 8 | 9 | set (_src_all_cxx ${_src_cxx} ${_src_looper_cxx}) 10 | 11 | if (NOT MINGW) 12 | add_precompiled_header (stdafx.h _src_all_cxx) 13 | endif() 14 | 15 | add_executable (examples ${_src_all_cxx} ${_src_h} ${_src_looper_h}) 16 | 17 | 18 | target_link_libraries (examples cpp_await) 19 | 20 | message ("Boost linked libraries: ${Boost_LIBRARIES}") 21 | target_link_libraries (examples ${Boost_LIBRARIES}) 22 | 23 | if (OPENSSL_FOUND) 24 | message ("OpenSSL linked libraries: ${OPENSSL_LIBRARIES}") 25 | target_link_libraries (examples ${OPENSSL_LIBRARIES}) 26 | endif() 27 | 28 | if (WIN32) 29 | target_link_libraries (examples ws2_32 mswsock) 30 | elseif (UNIX) 31 | target_link_libraries (examples rt pthread) 32 | endif() 33 | 34 | if (MSVC) 35 | set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") 36 | endif() 37 | -------------------------------------------------------------------------------- /Examples/ExUtil.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #ifdef _MSC_VER 23 | 24 | #ifdef NDEBUG 25 | # define _SECURE_SCL 0 26 | #endif 27 | 28 | #ifndef _SCL_SECURE_NO_WARNINGS 29 | # define _SCL_SECURE_NO_WARNINGS 30 | #endif 31 | 32 | #ifndef _CRT_SECURE_NO_WARNINGS 33 | # define _CRT_SECURE_NO_WARNINGS 34 | #endif 35 | 36 | #ifndef NOMINMAX 37 | # define NOMINMAX 38 | #endif 39 | 40 | #endif // _MSC_VER 41 | 42 | 43 | // borrow some helpers from library 44 | // 45 | #include 46 | #include 47 | 48 | // borrow foreach 49 | // 50 | #define foreach_ ut_foreach_ 51 | 52 | 53 | // 54 | // extra 55 | // 56 | 57 | inline const char* readLine() 58 | { 59 | static char line[512]; 60 | 61 | void* result = fgets(line, sizeof(line), stdin); 62 | 63 | if (result == nullptr) { 64 | line[0] = '\0'; 65 | } else { 66 | line[strlen(line) - 1] = '\0'; 67 | } 68 | 69 | return line; 70 | } 71 | -------------------------------------------------------------------------------- /Examples/Looper/Chrono.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "../ExUtil.h" 18 | #include "Chrono.h" 19 | 20 | namespace loo { 21 | 22 | static Timepoint sBaseTime; 23 | 24 | void rebaseMonotonicTime() 25 | { 26 | sBaseTime = lchrono::time_point_cast( 27 | Timepoint::clock::now()); 28 | } 29 | 30 | Timepoint getMonotonicTime() 31 | { 32 | return Timepoint( 33 | lchrono::duration_cast( 34 | Timepoint::clock::now() - sBaseTime)); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Examples/Looper/Chrono.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include "Thread.h" 21 | 22 | namespace loo { 23 | 24 | typedef lchrono::time_point Timepoint; 25 | 26 | // 27 | // utility functions 28 | // 29 | 30 | void rebaseMonotonicTime(); 31 | 32 | Timepoint getMonotonicTime(); 33 | 34 | inline long long getMonotonicMicroseconds() 35 | { 36 | return getMonotonicTime().time_since_epoch().count(); 37 | } 38 | 39 | inline long getMonotonicMilliseconds() 40 | { 41 | return (long) (getMonotonicMicroseconds() / 1000LL); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Examples/Looper/Looper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "../ExUtil.h" 18 | #include "Looper.h" 19 | #include 20 | 21 | namespace loo { 22 | 23 | static Looper* sMainLooper; 24 | 25 | Looper& mainLooper() 26 | { 27 | return *sMainLooper; 28 | } 29 | 30 | void setMainLooper(Looper& mainLooper) 31 | { 32 | sMainLooper = &mainLooper; 33 | } 34 | 35 | Looper::Looper(const std::string& name) 36 | : mName(name) 37 | , mQuit(false) 38 | { 39 | } 40 | 41 | Looper::~Looper() 42 | { 43 | } 44 | 45 | void Looper::run() 46 | { 47 | mThreadId = lthread::this_thread::get_id(); 48 | 49 | mQuit = false; 50 | do { 51 | { UniqueLock lock(mMutex); 52 | do { 53 | Timepoint sleepUntil = mContext.queuePending(); 54 | Timepoint now = getMonotonicTime(); 55 | 56 | if (sleepUntil <= now) { 57 | break; 58 | } 59 | Timepoint::duration timeout = sleepUntil - now; 60 | 61 | if (timeout < lchrono::milliseconds(2)) { // busy wait if less than 2ms until trigger 62 | do { 63 | mMutex.unlock(); 64 | lthread::this_thread::yield(); 65 | mMutex.lock(); 66 | } while (getMonotonicTime() < sleepUntil && !mContext.hasPending()); 67 | } else { 68 | mMutexCond.wait_for(lock, timeout); 69 | } 70 | } while (true); 71 | } 72 | 73 | assert (!mQuit); 74 | 75 | mContext.runQueued(&mQuit); 76 | 77 | lthread::this_thread::yield(); 78 | } while (!mQuit); 79 | 80 | { LockGuard _(mMutex); 81 | mContext.queuePending(); // delete cancelled actions 82 | } 83 | } 84 | 85 | void Looper::quit() 86 | { 87 | assert (lthread::this_thread::get_id() == mThreadId && "quit() called from outside the loop!"); 88 | 89 | cancelAll(); 90 | mQuit = true; 91 | } 92 | 93 | bool Looper::cancel(Ticket ticket) 94 | { 95 | assert (lthread::this_thread::get_id() == mThreadId && "tryCancel() called from outside the loop!"); 96 | 97 | bool didCancel = mContext.tryCancelQueued(ticket); 98 | 99 | if (!didCancel) { 100 | { LockGuard _(mMutex); 101 | didCancel = mContext.tryCancelPending(ticket); 102 | } 103 | } 104 | 105 | return didCancel; 106 | } 107 | 108 | void Looper::cancelAll() 109 | { 110 | assert (lthread::this_thread::get_id() == mThreadId && "cancelAll() called from outside the loop!"); 111 | 112 | mContext.cancelAllQueued(); 113 | 114 | { LockGuard _(mMutex); 115 | mContext.cancelAllPending(); 116 | } 117 | } 118 | 119 | // 120 | // detail 121 | // 122 | 123 | namespace detail 124 | { 125 | struct LoopContext::ManagedAction 126 | { 127 | ManagedAction(Ticket ticket, RepeatingAction&& action, lchrono::milliseconds interval, bool catchUp) 128 | : ticket(ticket), action(std::move(action)), interval(interval), catchUp(catchUp), isCancelled(false) { } 129 | 130 | Ticket ticket; 131 | RepeatingAction action; 132 | lchrono::milliseconds interval; 133 | bool catchUp; 134 | Timepoint triggerTime; 135 | bool isCancelled; 136 | }; 137 | 138 | LoopContext::LoopContext() 139 | : mTicketCounter(100) 140 | { 141 | } 142 | 143 | LoopContext::~LoopContext() 144 | { 145 | } 146 | 147 | void LoopContext::runQueued(bool *quit) 148 | { 149 | Timepoint now = getMonotonicTime(); 150 | 151 | ut_foreach_(ManagedAction *action, mQueuedActions) { 152 | if (action->isCancelled) 153 | continue; 154 | 155 | if (action->triggerTime <= now) { 156 | bool repeat = false; 157 | try { 158 | repeat = action->action(); 159 | } catch(const std::exception& ex) { 160 | fprintf(stderr, "Uncaught exception while running loop action: %s\n", ex.what()); 161 | throw; 162 | } 163 | 164 | if (repeat) { 165 | if (action->catchUp) { 166 | action->triggerTime += action->interval; 167 | } else { 168 | action->triggerTime = now + action->interval; 169 | } 170 | } else { 171 | action->isCancelled = true; 172 | } 173 | 174 | if (*quit) { // running the action may have triggered quit 175 | break; 176 | } 177 | } 178 | } 179 | } 180 | 181 | Timepoint LoopContext::queuePending() // must have lock 182 | { 183 | ut_foreach_(ManagedAction *action, mQueuedActions) { 184 | if (action->isCancelled) { 185 | delete action; 186 | } else { 187 | mPendingActions.push_back(action); 188 | } 189 | } 190 | 191 | mQueuedActions.clear(); 192 | mQueuedActions.swap(mPendingActions); 193 | 194 | Timepoint wakeTime = Timepoint::max(); 195 | 196 | ut_foreach_(ManagedAction *action, mQueuedActions) { 197 | if (action->triggerTime < wakeTime) { 198 | wakeTime = action->triggerTime; 199 | } 200 | } 201 | 202 | return wakeTime; 203 | } 204 | 205 | bool LoopContext::hasPending() // must have lock 206 | { 207 | return !mPendingActions.empty(); 208 | } 209 | 210 | bool LoopContext::tryCancelQueued(Ticket ticket) 211 | { 212 | ut_foreach_(ManagedAction *action, mQueuedActions) { 213 | if (action->ticket == ticket) { 214 | if (action->isCancelled) { 215 | return false; 216 | } else { 217 | action->isCancelled = true; 218 | return true; 219 | } 220 | } 221 | } 222 | 223 | return false; 224 | } 225 | 226 | bool LoopContext::tryCancelPending(Ticket ticket) // must have lock 227 | { 228 | for (std::vector::iterator it = mPendingActions.begin(), end = mPendingActions.end(); it != end; ++it) { 229 | ManagedAction *action = *it; 230 | 231 | if (action->ticket == ticket) { 232 | assert (!action->isCancelled); 233 | 234 | delete action; 235 | mPendingActions.erase(it); 236 | return true; 237 | } 238 | } 239 | 240 | return false; 241 | } 242 | 243 | void LoopContext::cancelAllQueued() 244 | { 245 | ut_foreach_(ManagedAction *action, mQueuedActions) { 246 | action->isCancelled = true; 247 | } 248 | } 249 | 250 | void LoopContext::cancelAllPending() // must have lock 251 | { 252 | ut_foreach_(ManagedAction *action, mPendingActions) { 253 | delete action; 254 | } 255 | mPendingActions.clear(); 256 | } 257 | 258 | Ticket LoopContext::scheduleImpl(RepeatingAction&& action, Timepoint triggerTime, lchrono::milliseconds interval, bool catchUp) 259 | { 260 | ManagedAction *sa = new ManagedAction(++mTicketCounter, std::move(action), interval, catchUp); 261 | sa->triggerTime = triggerTime; 262 | 263 | mPendingActions.push_back(sa); 264 | 265 | return sa->ticket; 266 | } 267 | } 268 | 269 | } 270 | -------------------------------------------------------------------------------- /Examples/Looper/Looper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include "Chrono.h" 21 | #include "Thread.h" 22 | #include 23 | #include 24 | #include 25 | 26 | namespace loo { 27 | 28 | typedef std::function RepeatingAction; 29 | 30 | typedef int Ticket; 31 | 32 | // 33 | // detail 34 | // 35 | 36 | namespace detail 37 | { 38 | class LoopContext 39 | { 40 | public: 41 | LoopContext(); 42 | ~LoopContext(); 43 | 44 | void runQueued(bool *quit); 45 | 46 | Timepoint queuePending(); // must have lock 47 | 48 | bool hasPending(); // must have lock 49 | 50 | template 51 | Ticket schedule(Callable&& action, Timepoint triggerTime) // must have lock 52 | { 53 | // static_assert(std::is_convertible >::value, "Action signature must be: () -> void"); 54 | static_assert(std::is_void::value, "Action must return void! Use scheduleRepeating for repeating actions."); 55 | 56 | return scheduleImpl(asRepeatingAction(std::forward(action)), triggerTime, lchrono::milliseconds(0), false); 57 | } 58 | 59 | template 60 | Ticket scheduleRepeating(Predicate&& action, Timepoint triggerTime, lchrono::milliseconds interval, bool catchUp) // must have lock 61 | { 62 | // static_assert(std::is_convertible >::value, "Repeating action signature must be: () -> bool"); 63 | 64 | return scheduleImpl(std::move(action), triggerTime, interval, catchUp); 65 | } 66 | 67 | bool tryCancelQueued(Ticket ticket); 68 | 69 | bool tryCancelPending(Ticket ticket); // must have lock 70 | 71 | void cancelAllQueued(); 72 | 73 | void cancelAllPending(); // must have lock 74 | 75 | private: 76 | struct ManagedAction; 77 | 78 | template 79 | static RepeatingAction asRepeatingAction(Callable&& callable) 80 | { 81 | struct Wrapper 82 | { 83 | typedef typename std::decay::type RawCallable; 84 | 85 | Wrapper(const RawCallable& callable) 86 | : callable(callable) { } 87 | 88 | Wrapper(RawCallable&& callable) 89 | : callable(std::move(callable)) { } 90 | 91 | bool operator()() 92 | { 93 | this->callable(); 94 | return false; 95 | } 96 | 97 | RawCallable callable; 98 | }; 99 | 100 | return Wrapper(std::forward(callable)); 101 | } 102 | 103 | Ticket scheduleImpl(RepeatingAction&& action, Timepoint triggerTime, lchrono::milliseconds interval, bool catchUp); 104 | 105 | int mTicketCounter; 106 | 107 | std::vector mQueuedActions; 108 | std::vector mPendingActions; 109 | }; 110 | } 111 | 112 | // 113 | // Looper 114 | // 115 | 116 | class Looper 117 | { 118 | public: 119 | Looper(const std::string& name); 120 | 121 | ~Looper(); 122 | 123 | void run(); 124 | 125 | void quit(); 126 | 127 | bool cancel(Ticket ticket); 128 | 129 | void cancelAll(); 130 | 131 | /** thread safe */ 132 | template 133 | Ticket schedule(Callable&& action, long delay = 0) 134 | { 135 | Timepoint triggerTime = getMonotonicTime() + lchrono::milliseconds(delay); 136 | 137 | { LockGuard _(mMutex); 138 | mMutexCond.notify_one(); 139 | return mContext.schedule(std::forward(action), triggerTime); 140 | } 141 | } 142 | 143 | /** thread safe */ 144 | template 145 | Ticket scheduleRepeating(Predicate&& action, long delay = 0, long interval = 0, bool catchUp = false) 146 | { 147 | Timepoint triggerTime = getMonotonicTime() + lchrono::milliseconds(delay); 148 | 149 | { LockGuard _(mMutex); 150 | mMutexCond.notify_one(); 151 | return mContext.scheduleRepeating(std::forward(action), triggerTime, lchrono::milliseconds(interval), catchUp); 152 | } 153 | 154 | } 155 | 156 | private: 157 | typedef lthread::timed_mutex Mutex; 158 | typedef lthread::lock_guard LockGuard; 159 | typedef lthread::unique_lock UniqueLock; 160 | 161 | detail::LoopContext mContext; 162 | 163 | Mutex mMutex; 164 | lthread::condition_variable_any mMutexCond; 165 | 166 | std::string mName; 167 | 168 | lthread::thread::id mThreadId; 169 | 170 | bool mQuit; 171 | }; 172 | 173 | void setMainLooper(Looper &mainLooper); 174 | 175 | Looper& mainLooper(); 176 | 177 | } 178 | -------------------------------------------------------------------------------- /Examples/Looper/Thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | #define UT_HAVE_STD_THREAD 22 | 23 | // MSVC10 doesn't implement chrono & thread, fallback to Boost 24 | // 25 | #ifdef BOOST_MSVC 26 | # if (BOOST_MSVC < 1700) 27 | # undef UT_HAVE_STD_THREAD 28 | # endif 29 | #endif 30 | 31 | // regular MINGW doesn't implement chrono & thread, fallback to Boost 32 | // 33 | #ifdef __MINGW32__ 34 | # undef UT_HAVE_STD_THREAD 35 | #endif 36 | 37 | #ifdef UT_HAVE_STD_THREAD 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | namespace loo { 45 | namespace lchrono { 46 | using namespace std::chrono; 47 | } 48 | 49 | namespace lthread { 50 | using namespace std; 51 | } 52 | } 53 | 54 | #else 55 | 56 | #include 57 | #include 58 | 59 | namespace loo { 60 | namespace lchrono { 61 | using namespace boost::chrono; 62 | } 63 | 64 | namespace lthread { 65 | using namespace boost; 66 | } 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /Examples/ex_awaitBasics.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include 19 | #include 20 | #include 21 | 22 | // 23 | // ABOUT: how to define, use & combine Awaitables 24 | // 25 | 26 | // run loop 27 | static boost::asio::io_service sIo; 28 | 29 | 30 | // simple awaitable without coroutine 31 | // 32 | static ut::Awaitable asyncSimpleDelay(long delay) 33 | { 34 | // on calling coroutine 35 | 36 | ut::Awaitable awt; 37 | 38 | // awaitables can be tagged to ease debugging 39 | awt.setTag(ut::string_printf("simple-delay-%ld", delay)); 40 | 41 | // Schedule completion after delay milliseconds. Exactly what triggers 42 | // completion is an implementation detail -- here we use an Asio 43 | // deadline_timer. The only thing that matters is to call complete() 44 | // from master coroutine (i.e. your main loop). 45 | 46 | auto timer = new boost::asio::deadline_timer( 47 | sIo, boost::posix_time::milliseconds(delay)); 48 | 49 | ut::Completer completer = awt.takeCompleter(); 50 | 51 | timer->async_wait([completer](const boost::system::error_code& ec) { 52 | // on master coroutine (io_service) 53 | 54 | // Awaitable may have been destroyed and the timer interrupted 55 | // (ec = error::operation_aborted), in which case completer 56 | // does nothing. 57 | 58 | completer(); // yields to awaiting coroutine unless done 59 | }); 60 | 61 | // cancel timer if interrupted 62 | awt.then([timer]() { 63 | delete timer; // posts error::operation_aborted 64 | }); 65 | 66 | return std::move(awt); 67 | } 68 | 69 | // Awaitable with dedicated coroutine. While in a coroutine you may yield. 70 | // await() simply means 'yield until task is done'. It does not block, instead 71 | // it yields to main loop if necessary until task done. 72 | // 73 | static ut::Awaitable asyncCoroDelay(long delay) 74 | { 75 | // on calling coroutine 76 | std::string tag = ut::string_printf("coro-delay-%ld", delay); 77 | 78 | return ut::startAsync(tag, [=]() { 79 | // on 'coro-delay' coroutine 80 | printf ("'%s' - start\n", ut::currentCoro()->tag()); 81 | 82 | ut::Awaitable awt = asyncSimpleDelay(delay); 83 | 84 | // free to do other stuff... 85 | 86 | awt.await(); // yield until awt done 87 | 88 | printf ("'%s' - done\n", ut::currentCoro()->tag()); 89 | }); 90 | } 91 | 92 | // test coroutine 93 | // 94 | static ut::Awaitable asyncTest() 95 | { 96 | return ut::startAsync("test", []() { 97 | // on 'test' coroutine 98 | printf ("'%s' - start\n", ut::currentCoro()->tag()); 99 | 100 | // it's trivial to compose awaitables 101 | std::array awts = { { 102 | asyncSimpleDelay(400), 103 | asyncCoroDelay(300), 104 | asyncCoroDelay(800) 105 | } }; 106 | ut::awaitAll(awts); 107 | 108 | printf ("'%s' - done\n", ut::currentCoro()->tag()); 109 | 110 | sIo.stop(); 111 | }); 112 | } 113 | 114 | void ex_awaitBasics() 115 | { 116 | ut::Awaitable awtTest = asyncTest(); 117 | 118 | // print every 100ms to show main loop is not blocked 119 | ut::Awaitable awtTicker = ut::startAsync("ticker", []() { 120 | while (true) { 121 | ut::Awaitable awt = asyncSimpleDelay(100); 122 | awt.await(); 123 | 124 | printf ("...\n"); 125 | } 126 | }); 127 | 128 | printf ("'%s' - START\n", ut::currentCoro()->tag()); 129 | 130 | // Usually there needs to be a run loop to complete Awaitables. This is 131 | // application specific (Qt / GLib / MFC / Asio ...) You may want to wrap 132 | // it inside a generic scheduler (see ut::initScheduler()). 133 | sIo.run(); 134 | 135 | printf ("'%s' - END\n", ut::currentCoro()->tag()); 136 | 137 | // Main routine may not be suspended, so await() is permitted only if Awaitable 138 | // is done. Calling await() this way lets you check for unhandled exceptions. 139 | // 140 | try { 141 | awtTest.await(); 142 | } catch (const std::exception& e) { 143 | printf ("crash: %s\n", e.what()); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Examples/ex_awaitChatClient.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include 19 | #include "Looper/Thread.h" 20 | #include 21 | 22 | // 23 | // ABOUT: chat client, similar to Asio chat example 24 | // 25 | 26 | using namespace boost::asio::ip; 27 | using namespace loo::lthread; 28 | using namespace loo::lchrono; 29 | 30 | 31 | typedef std::shared_ptr MessageCRef; 32 | 33 | 34 | // run loop 35 | static boost::asio::io_service sIo; 36 | 37 | // holds outbound messages 38 | static std::queue sMsgQueue; 39 | 40 | // used to notify when a new message has been queued 41 | static ut::Completer sEvtMsgQueued; 42 | 43 | // reads keyboard input and enqueues outbound messages 44 | static void inputFunc() 45 | { 46 | // sleep to tidy up output 47 | this_thread::sleep_for(chrono::milliseconds(50)); 48 | printf (" > "); 49 | 50 | do { 51 | std::string line = readLine(); 52 | 53 | // process the message on main loop 54 | sIo.post([line]() { 55 | sMsgQueue.push(std::make_shared(line + "\n")); 56 | 57 | // wake up writer 58 | sEvtMsgQueued(); 59 | }); 60 | 61 | // sleep to tidy up output 62 | this_thread::sleep_for(chrono::milliseconds(50)); 63 | printf (" > "); 64 | } while(true); 65 | } 66 | 67 | static ut::Awaitable asyncChatClient(const std::string& host, const std::string& port, const std::string& nickname) 68 | { 69 | // this coroutine reads & prints inbound messages 70 | auto reader = [](tcp::socket& socket) { 71 | auto recv = std::make_shared(); 72 | std::string msg; 73 | 74 | do { 75 | ut::Awaitable awt = ut::asio::asyncReadUntil(socket, recv, std::string("\n")); 76 | awt.await(); // yield until we have an inbound message 77 | 78 | std::istream recvStream(recv.get()); 79 | std::getline(recvStream, msg); 80 | 81 | printf ("-- %s\n", msg.c_str()); 82 | } while (true); 83 | }; 84 | 85 | // this coroutine writes outbound messages 86 | auto writer = [](tcp::socket& socket) { 87 | bool quit = false; 88 | 89 | do { 90 | if (sMsgQueue.empty()) { 91 | ut::Awaitable awtMsgQueued("evt-msg-queued"); 92 | sEvtMsgQueued = awtMsgQueued.takeCompleter(); 93 | 94 | awtMsgQueued.await(); // yield until we have outbound messages 95 | } else { 96 | MessageCRef msg = sMsgQueue.front(); 97 | sMsgQueue.pop(); 98 | 99 | ut::Awaitable awt = ut::asio::asyncWrite(socket, msg); 100 | awt.await(); // yield until message delivered 101 | 102 | if (*msg == "/leave\n") { 103 | quit = true; 104 | } 105 | } 106 | } while (!quit); 107 | }; 108 | 109 | // main coroutine handles connection, handshake, reads & writes 110 | return ut::startAsync("asyncChatClient", [host, port, nickname, reader, writer]() { 111 | try { 112 | ut::Awaitable awt; 113 | 114 | tcp::socket socket(sIo); 115 | 116 | tcp::resolver resolver(sIo); 117 | tcp::resolver::query query(tcp::v4(), host, port); 118 | 119 | tcp::resolver::iterator itEndpoints; 120 | awt = ut::asio::asyncResolve(resolver, query, itEndpoints); 121 | awt.await(); 122 | 123 | tcp::resolver::iterator itConnected; 124 | awt = ut::asio::asyncConnect(socket, itEndpoints, itConnected); 125 | awt.await(); 126 | 127 | // Asio wrappers need some arguments passed as shared_ptr in order to support safe interruption 128 | auto msg = std::make_shared(nickname.begin(), nickname.end()); 129 | // all messages end with newline 130 | msg->push_back('\n'); 131 | 132 | // first outbound message is always nickname 133 | awt = ut::asio::asyncWrite(socket, msg); 134 | awt.await(); 135 | 136 | // read keyboard input on a different thread to keep main loop responsive 137 | thread inputThread(&inputFunc); 138 | inputThread.detach(); 139 | 140 | // this coroutine reads & prints inbound messages 141 | ut::Awaitable awtReader = ut::startAsync("chatClient-reader", [=, &socket]() { 142 | reader(socket); 143 | }); 144 | 145 | // this coroutine writes outbound messages 146 | ut::Awaitable awtWriter = ut::startAsync("chatClient-writer", [=, &socket]() { 147 | writer(socket); 148 | }); 149 | 150 | // quit on /leave or Asio exception 151 | ut::Awaitable *done = ut::awaitAny(awtReader, awtWriter); 152 | 153 | // cancel socket operations? 154 | // socket.shutdown(tcp::socket::shutdown_both); 155 | 156 | // trigger exception, if any 157 | done->await(); 158 | } catch (const std::exception& ex) { 159 | printf ("Failed! %s - %s\n", typeid(ex).name(), ex.what()); 160 | } catch (...) { 161 | printf ("Failed!\n"); 162 | } 163 | }); 164 | } 165 | 166 | void ex_awaitChatClient() 167 | { 168 | printf ("your nickname: "); 169 | std::string nickname = readLine(); 170 | 171 | ut::Awaitable awt = asyncChatClient("localhost", "3455", nickname); 172 | 173 | // loops until all async handlers have ben dispatched 174 | sIo.run(); 175 | } 176 | -------------------------------------------------------------------------------- /Examples/ex_awaitChatServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // 24 | // ABOUT: chat server, similar to Asio chat example 25 | // 26 | 27 | using namespace boost::asio::ip; 28 | 29 | 30 | typedef std::shared_ptr MessageCRef; 31 | 32 | 33 | // run loop 34 | static boost::asio::io_service sIo; 35 | 36 | 37 | inline MessageCRef packMessage(const std::string& sender, const std::string& line) 38 | { 39 | // chat messages are delimited by newline 40 | return std::make_shared( 41 | ut::string_printf("%s: %s\n", sender.c_str(), line.c_str())); 42 | } 43 | 44 | // chat guest interface 45 | class Guest 46 | { 47 | public: 48 | virtual ~Guest() { } 49 | 50 | virtual const std::string& nickname() = 0; 51 | 52 | virtual void deliver(MessageCRef msg) = 0; 53 | }; 54 | 55 | // chat room manages guests and message delivery 56 | class ChatRoom 57 | { 58 | public: 59 | void join(Guest *guest) 60 | { 61 | mGuests.insert(guest); 62 | 63 | // deliver recent history 64 | ut_foreach_(auto& msg, mRecentMsgs) { 65 | guest->deliver(msg); 66 | } 67 | 68 | std::string line = ut::string_printf("%s has joined", guest->nickname().c_str()); 69 | printf ("%s\n", line.c_str()); 70 | 71 | // notify all guests 72 | broadcast(":server", line); 73 | } 74 | 75 | void leave(Guest *guest) 76 | { 77 | auto pos = mGuests.find(guest); 78 | 79 | if (pos != mGuests.end()) { 80 | mGuests.erase(pos); 81 | 82 | std::string line = ut::string_printf("%s has left", guest->nickname().c_str()); 83 | printf ("%s\n", line.c_str()); 84 | 85 | // notify all clients 86 | broadcast(":server", line); 87 | } 88 | } 89 | 90 | void broadcast(const std::string& sender, const std::string& line) 91 | { 92 | MessageCRef msgRef = packMessage(sender, line); 93 | 94 | if (MAX_RECENT_MSGS > 0) { 95 | if (mRecentMsgs.size() == MAX_RECENT_MSGS) { 96 | mRecentMsgs.pop_front(); 97 | } 98 | 99 | mRecentMsgs.push_back(msgRef); 100 | } 101 | 102 | ut_foreach_(Guest *guest, mGuests) { 103 | guest->deliver(msgRef); 104 | } 105 | } 106 | 107 | private: 108 | static const size_t MAX_RECENT_MSGS = 10; 109 | 110 | std::set mGuests; 111 | std::deque mRecentMsgs; 112 | }; 113 | 114 | // manages client coroutine 115 | class ClientSession : public Guest 116 | { 117 | public: 118 | ClientSession(ChatRoom& room) 119 | : mRoom(room) 120 | , mSocket(std::make_shared(sIo)) { } 121 | 122 | // deleting the session will interrupt the coroutine 123 | ~ClientSession() { } 124 | 125 | const std::string& nickname() // override 126 | { 127 | return mNickname; 128 | } 129 | 130 | void deliver(MessageCRef msg) // override 131 | { 132 | mMsgQueue.push(msg); 133 | 134 | { ut::PushMasterCoro _; // take over 135 | // wake up writer 136 | mEvtMsgQueued(); 137 | } 138 | } 139 | 140 | std::shared_ptr socket() 141 | { 142 | return mSocket; 143 | } 144 | 145 | ut::Awaitable& awaitable() 146 | { 147 | return mAwt; 148 | } 149 | 150 | void start() 151 | { 152 | auto recv = std::make_shared(); 153 | 154 | // this coroutine writes outbound messages 155 | ut::Action writer = [this]() { 156 | do { 157 | if (mMsgQueue.empty()) { 158 | ut::Awaitable awtMsgQueued("evt-msg-queued"); 159 | mEvtMsgQueued = awtMsgQueued.takeCompleter(); 160 | 161 | awtMsgQueued.await(); // yield until we have outbound messages 162 | } else { 163 | MessageCRef msg = mMsgQueue.front(); 164 | mMsgQueue.pop(); 165 | 166 | ut::Awaitable awt = ut::asio::asyncWrite(*mSocket, msg); 167 | awt.await(); // yield until message delivered 168 | } 169 | } while (true); 170 | }; 171 | 172 | // this coroutine reads inbound messages 173 | ut::Action reader = [this, recv]() { 174 | std::string line; 175 | 176 | bool quit = false; 177 | do { 178 | ut::Awaitable awt = ut::asio::asyncReadUntil(*mSocket, recv, std::string("\n")); 179 | awt.await(); // yield until we have inbound messages 180 | 181 | std::istream recvStream(recv.get()); 182 | std::getline(recvStream, line); 183 | 184 | if (line == "/leave") { 185 | quit = true; 186 | } else { 187 | mRoom.broadcast(mNickname, line); 188 | } 189 | } while (!quit); 190 | }; 191 | 192 | // main coroutine handles handshake, reads & writes 193 | mAwt = ut::startAsync("clientSession-start", [this, writer, reader, recv]() { 194 | // first message is nickname 195 | ut::Awaitable awt = ut::asio::asyncReadUntil(*mSocket, recv, std::string("\n")); 196 | awt.await(); 197 | std::istream recvStream(recv.get()); 198 | std::getline(recvStream, mNickname); 199 | 200 | mRoom.join(this); 201 | 202 | ut::Awaitable awtReader = ut::startAsync("clientSession-reader", reader); 203 | ut::Awaitable awtWriter = ut::startAsync("clientSession-writer", writer); 204 | 205 | // yield until /leave or Asio exception 206 | ut::Awaitable *done = ut::awaitAny(awtReader, awtWriter); 207 | 208 | mRoom.leave(this); 209 | 210 | done->await(); // check for exception, won't yield again since already done 211 | }); 212 | } 213 | 214 | private: 215 | ChatRoom& mRoom; 216 | 217 | std::shared_ptr mSocket; 218 | ut::Awaitable mAwt; 219 | std::string mNickname; 220 | 221 | std::queue mMsgQueue; 222 | ut::Completer mEvtMsgQueued; 223 | }; 224 | 225 | // attribute shim for acessing the Awaitable of a ClientSession, used by ut::asyncAny() 226 | inline ut::Awaitable* selectAwaitable(std::unique_ptr& element) 227 | { 228 | return &(element->awaitable()); 229 | } 230 | 231 | static ut::Awaitable asyncChatServer(unsigned short port) 232 | { 233 | // main coroutine manages client sessions 234 | return ut::startAsync("asyncChatServer", [port]() { 235 | typedef std::list > SessionList; 236 | 237 | ChatRoom room; 238 | SessionList mSessions; 239 | 240 | tcp::endpoint endpoint(tcp::v4(), port); 241 | 242 | tcp::acceptor acceptor(sIo, endpoint.protocol()); 243 | try { 244 | acceptor.bind(endpoint); 245 | acceptor.listen(); 246 | } catch (...) { 247 | printf ("Couldn't bind to port %u.\n", port); 248 | return; 249 | } 250 | 251 | std::unique_ptr session; 252 | ut::Awaitable awtAccept; 253 | 254 | while (true) { 255 | printf ("waiting for clients to connect / disconnect...\n"); 256 | 257 | if (!session) { 258 | // prepare for new connection 259 | session.reset(new ClientSession(room)); 260 | awtAccept = ut::asio::asyncAccept(acceptor, session->socket()); 261 | } 262 | 263 | SessionList::iterator posTerminated; 264 | 265 | // combine the list of awaitables for easier manipulation 266 | ut::Awaitable awtSessionTerminated = ut::asyncAny(mSessions, posTerminated); 267 | 268 | // yield until a new connection has been accepted / terminated 269 | ut::Awaitable *done = ut::awaitAny(awtAccept, awtSessionTerminated); 270 | 271 | if (done == &awtAccept) { 272 | try { 273 | awtAccept.await(); // check for exception 274 | printf ("client acepted\n"); 275 | 276 | // start session coroutine 277 | session->start(); 278 | mSessions.push_back(std::move(session)); 279 | } catch(...) { 280 | printf ("failed to accept client\n"); 281 | } 282 | 283 | session = nullptr; 284 | } else { 285 | // remove terminated session 286 | ClientSession *pSession = posTerminated->get(); 287 | 288 | try { 289 | pSession->awaitable().await(); // check for exception 290 | printf ("client '%s' has left\n", pSession->nickname().c_str()); 291 | } catch(...) { 292 | printf ("client '%s' disconnected\n", pSession->nickname().c_str()); 293 | } 294 | 295 | mSessions.erase(posTerminated); 296 | } 297 | } 298 | }); 299 | } 300 | 301 | void ex_awaitChatServer() 302 | { 303 | ut::Awaitable awt = asyncChatServer(3455); 304 | 305 | // loops until all async handlers have ben dispatched 306 | sIo.run(); 307 | } 308 | -------------------------------------------------------------------------------- /Examples/ex_awaitFlickr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifdef HAVE_OPENSSL 18 | 19 | #include "ExUtil.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // 30 | // ABOUT: fetch pictures from Flickr 31 | // 32 | 33 | using namespace boost::asio::ip; 34 | namespace ssl = boost::asio::ssl; 35 | 36 | // run loop 37 | static boost::asio::io_service sIo; 38 | 39 | // 40 | // flickr utils 41 | // 42 | 43 | static const std::string FLICKR_API_HOST = "api.flickr.com"; 44 | static const std::string FLICKR_API_KEY = "e36784df8a03fea04c22ed93318b291c"; 45 | 46 | struct FlickrPhoto 47 | { 48 | std::string id; 49 | std::string owner; 50 | std::string secret; 51 | std::string server; 52 | std::string farm; 53 | std::string title; 54 | }; 55 | 56 | struct FlickrPhotos 57 | { 58 | int page; 59 | int pages; 60 | int perPage; 61 | int total; 62 | std::vector photos; 63 | }; 64 | 65 | static FlickrPhotos parseFlickrResponse(boost::asio::streambuf& response) 66 | { 67 | namespace pt = boost::property_tree; 68 | 69 | FlickrPhotos result; 70 | 71 | std::stringstream ss; 72 | ss << &response; 73 | pt::ptree ptree; 74 | pt::read_xml(ss, ptree); 75 | 76 | const pt::ptree& resp = ptree.get_child("rsp"); 77 | const std::string& stat = resp.get(".stat", "error"); 78 | 79 | if (stat != "ok") { 80 | printf ("\nbad response:\n%s\n", ss.str().c_str()); 81 | throw std::runtime_error("flickr response not ok"); 82 | } 83 | 84 | foreach_(const auto& node, resp.get_child("photos")) { 85 | const pt::ptree& value = node.second; 86 | 87 | if (node.first == "") { 88 | result.page = value.get("page"); 89 | result.pages = value.get("pages"); 90 | result.perPage = value.get("perpage"); 91 | result.total = value.get("total"); 92 | } else { 93 | FlickrPhoto fp; 94 | fp.id = value.get(".id"); 95 | fp.owner = value.get(".owner"); 96 | fp.secret = value.get(".secret"); 97 | fp.server = value.get(".server"); 98 | fp.farm = value.get(".farm"); 99 | fp.title = value.get(".title"); 100 | 101 | result.photos.push_back(fp); 102 | } 103 | } 104 | 105 | return result; 106 | } 107 | 108 | static std::pair makeFlickrQueryUrl(const std::vector& tags, int perPage, int page) 109 | { 110 | std::string path = "/services/rest/?method=flickr.photos.search&format=rest&api_key=" + FLICKR_API_KEY; 111 | 112 | // std::string path = "api.flickr.com/services/rest/?method=flickr.photos.search&format=rest&api_key=e36784df8a03fea04c22ed93318b291c&tags=kitten 113 | // https://api.flickr.com/services/rest/?method=flickr.test.echo&name=value&api_key=e36784df8a03fea04c22ed93318b291c 114 | 115 | path += "&tags=" + tags[0]; 116 | for (size_t i = 1; i < tags.size(); i++) { 117 | path += "+" + tags[i]; 118 | } 119 | 120 | path += "&per_page=" + boost::lexical_cast(perPage); 121 | path += "&page=" + boost::lexical_cast(page); 122 | 123 | return std::make_pair(FLICKR_API_HOST, path); 124 | } 125 | 126 | static std::pair makeFlickrPhotoUrl(const FlickrPhoto& photo) 127 | { 128 | // format: http://farm{farm-id}.staticflickr.com/{server-id}/{id}_{secret}_[mstzb].jpg 129 | 130 | std::string host = "farm" + photo.farm + ".staticflickr.com"; 131 | std::string path = "/" + photo.server + "/" + photo.id + "_" + photo.secret + "_m.jpg"; 132 | 133 | return std::make_pair(host, path); 134 | } 135 | 136 | static ut::Awaitable asyncFlickrDownload(const std::vector& tags, int numPics, int numPicsPerPage) 137 | { 138 | static const int MAX_PARALLEL_DOWNLOADS = 6; 139 | 140 | return ut::startAsync("asyncFlickrDownload", [tags, numPics, numPicsPerPage]() { 141 | struct DownloadSlot 142 | { 143 | std::unique_ptr awaitable; 144 | std::shared_ptr buf; 145 | FlickrPhoto *photo; 146 | 147 | DownloadSlot() 148 | : buf(std::make_shared()) 149 | , photo(nullptr) { } 150 | }; 151 | 152 | typedef std::array DownloadSlots; 153 | 154 | try { 155 | int totalPicsRemaining = numPics; 156 | int page = 1; 157 | 158 | DownloadSlots dlslots; 159 | int numSlotsUsed = 0; 160 | 161 | auto startFetch = [&](DownloadSlots::iterator pos, FlickrPhoto& photo) { 162 | assert (pos->photo == nullptr); // slot must be available 163 | 164 | auto photoUrl = makeFlickrPhotoUrl(photo); 165 | printf (" fetching %s%s ...\n", photoUrl.first.c_str(), photoUrl.second.c_str()); 166 | 167 | pos->awaitable = ut::asUniquePtr(ut::asio::asyncHttpDownload(sIo, photoUrl.first, photoUrl.second, pos->buf)); 168 | pos->photo = &photo; 169 | 170 | numSlotsUsed++; 171 | totalPicsRemaining--; 172 | }; 173 | 174 | // prepare a persistent SSL connection for API queries 175 | typedef ssl::stream SslSocket; 176 | ssl::context ctx(ssl::context::sslv3_client); 177 | SslSocket apiSocket(sIo, ctx); 178 | // connect 179 | tcp::resolver::query query(FLICKR_API_HOST, "https"); 180 | tcp::resolver::iterator itConnected; 181 | ut::Awaitable awt = ut::asio::asyncResolveAndConnect(apiSocket.lowest_layer(), query, itConnected); 182 | awt.await(); 183 | apiSocket.lowest_layer().set_option(tcp::no_delay(true)); 184 | apiSocket.lowest_layer().set_option(boost::asio::socket_base::keep_alive(true)); 185 | // perform SSL handshake 186 | awt = ut::asio::asyncHandshake(apiSocket, SslSocket::client); 187 | awt.await(); 188 | 189 | while (totalPicsRemaining > 0) { 190 | // download a page 191 | auto queryUrl = makeFlickrQueryUrl(tags, numPicsPerPage, page); 192 | auto response = std::make_shared(); 193 | // read HTTP header 194 | size_t contentLength; 195 | awt = ut::asio::asyncHttpGet(apiSocket, queryUrl.first, queryUrl.second, true, response, contentLength); 196 | awt.await(); 197 | 198 | // parse xml 199 | FlickrPhotos resp = parseFlickrResponse(*response); 200 | 201 | printf ("query result: %ld photos, page %d/%d, %d per page, %d total\n", 202 | (long) resp.photos.size(), resp.page, resp.pages, resp.perPage, resp.total); 203 | 204 | int availablePicsRemaining = resp.total - (resp.page - 1) * resp.perPage; 205 | totalPicsRemaining = std::min(totalPicsRemaining, availablePicsRemaining); 206 | 207 | auto itPagePhotos = resp.photos.begin(); 208 | 209 | // download page photos in parallel (up to MAX_PARALLEL_DOWNLOADS) 210 | for (auto it = dlslots.begin(); it != dlslots.end(); ++it) { 211 | if (itPagePhotos == resp.photos.end() || totalPicsRemaining == 0) { 212 | break; 213 | } 214 | startFetch(it, *itPagePhotos++); 215 | } 216 | 217 | // advance as slots gets freed 218 | while (numSlotsUsed > 0) { 219 | DownloadSlots::iterator pos = ut::awaitAny(dlslots); 220 | pos->awaitable->await(); // won't yield again, just check for exception 221 | 222 | std::string savePath = pos->photo->id + ".jpg"; 223 | std::ofstream fout(savePath, std::ios::binary); 224 | fout << pos->buf.get(); 225 | printf (" saved %s\n", savePath.c_str()); 226 | 227 | // release slot 228 | pos->awaitable = nullptr; 229 | pos->photo = nullptr; 230 | numSlotsUsed--; 231 | 232 | if (itPagePhotos != resp.photos.end() && totalPicsRemaining > 0) { 233 | startFetch(pos, *itPagePhotos++); 234 | } 235 | } 236 | 237 | page++; 238 | } 239 | } catch (std::exception& e) { // exceptions get propagated into awaiting coroutine 240 | printf ("Download failed: %s - %s\n", typeid(e).name(), e.what()); 241 | } catch (...) { 242 | printf ("Download failed: unknown exception\n"); 243 | } 244 | }, 256 * 1024); // need stack > 64KB 245 | } 246 | 247 | void ex_awaitFlickr() 248 | { 249 | printf ("tags (default 'kitten'): "); 250 | std::string tags = readLine(); 251 | 252 | std::vector splitTags; 253 | boost::split(splitTags, tags, boost::is_space()); 254 | splitTags.resize( 255 | std::remove(splitTags.begin(), splitTags.end(), "") - splitTags.begin()); 256 | 257 | if (splitTags.empty()) { 258 | splitTags.push_back("kitten"); 259 | } 260 | 261 | ut::Awaitable awt = asyncFlickrDownload(splitTags, 25, 10); 262 | 263 | // loops until all async handlers have ben dispatched 264 | sIo.run(); 265 | } 266 | 267 | #endif // HAVE_OPENSSL 268 | -------------------------------------------------------------------------------- /Examples/ex_awaitHttpClient.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include "Looper/Looper.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // 26 | // ABOUT: download an HTTP file using boost::asio wrappers 27 | // 28 | 29 | using namespace boost::asio::ip; 30 | 31 | 32 | static boost::asio::io_service sIo; 33 | 34 | 35 | static void doAsyncHttpGetHeader(tcp::socket& socket, const std::string& host, const std::string& path, std::shared_ptr outResponse, size_t& outContentLength) 36 | { 37 | ut::Awaitable awt; 38 | 39 | tcp::resolver resolver(sIo); 40 | tcp::resolver::query query(host, "http"); 41 | 42 | // DNS resolve 43 | tcp::resolver::iterator itEndpoints; 44 | awt = ut::asio::asyncResolve(resolver, query, itEndpoints); 45 | 46 | printf ("resolving %s ...\n", host.c_str()); 47 | awt.await(); 48 | 49 | // connect 50 | for (tcp::resolver::iterator it = itEndpoints, end = tcp::resolver::iterator(); it != end; ++it) { 51 | tcp::endpoint ep = *it; 52 | awt = ut::asio::asyncConnect(socket, ep); 53 | 54 | printf ("attempting connect to %s ...\n", ep.address().to_string().c_str()); 55 | try { 56 | awt.await(); // connected! 57 | break; 58 | } catch (...) { 59 | // try next endpoint 60 | } 61 | } 62 | if (!socket.is_open()) { 63 | throw std::runtime_error("failed to connect socket"); 64 | } 65 | 66 | printf ("connected!\n"); 67 | 68 | auto request = std::make_shared(); 69 | 70 | // write HTTP request 71 | std::ostream requestStream(request.get()); 72 | requestStream << "GET " << path << " HTTP/1.1\r\n"; 73 | requestStream << "Host: " << host << "\r\n"; 74 | requestStream << "Accept: */*\r\n"; 75 | requestStream << "Connection: close\r\n\r\n"; 76 | 77 | awt = ut::asio::asyncWrite(socket, request); 78 | awt.await(); 79 | 80 | // read first response line 81 | awt = ut::asio::asyncReadUntil(socket, outResponse, std::string("\r\n")); 82 | awt.await(); 83 | 84 | std::istream responseStream(outResponse.get()); 85 | std::string httpVersion; 86 | responseStream >> httpVersion; 87 | int statusCode; 88 | responseStream >> statusCode; 89 | std::string statusMessage; 90 | std::getline(responseStream, statusMessage); 91 | 92 | if (!responseStream || !boost::starts_with(httpVersion, "HTTP/")) { 93 | throw std::runtime_error("invalid response"); 94 | } 95 | if (statusCode != 200) { 96 | throw std::runtime_error(ut::string_printf("bad status code: %d", statusCode)); 97 | } 98 | 99 | // read response headers 100 | awt = ut::asio::asyncReadUntil(socket, outResponse, std::string("\r\n\r\n")); 101 | awt.await(); 102 | 103 | // process headers 104 | std::string header; 105 | outContentLength = 0; 106 | 107 | printf ("headers:\n"); 108 | 109 | while (std::getline(responseStream, header)) { 110 | if (boost::starts_with(header, "Content-Length: ")) { 111 | auto l = header.substr(strlen("Content-Length: ")); 112 | l.resize(l.size() - 1); 113 | outContentLength = boost::lexical_cast(l); 114 | } 115 | 116 | printf (" %s\n", header.c_str()); 117 | 118 | if (header == "\r") { 119 | break; // done reading header fields 120 | } 121 | } 122 | } 123 | 124 | static ut::Awaitable asyncHttpDownload(const std::string& host, const std::string& path, const std::string& savePath) 125 | { 126 | return ut::startAsync("asyncHttpDownload", [host, path, savePath]() { 127 | tcp::socket socket(sIo); 128 | auto response = std::make_shared(); 129 | size_t contentLength; 130 | size_t numBytesTransferred; 131 | 132 | try { 133 | // read header. it's fine to yield from inner function 134 | doAsyncHttpGetHeader(socket, host, path, response, contentLength); 135 | 136 | // transfer remaining content 137 | ut::Awaitable awt = ut::asio::asyncRead(socket, response, 138 | boost::asio::transfer_exactly(contentLength - response->size()), numBytesTransferred); 139 | awt.await(); 140 | 141 | printf("saving %ld bytes to file '%s' ...\n", (long) response->size(), savePath.c_str()); 142 | 143 | // TODO: file I/O should also be async 144 | std::ofstream fout(savePath.c_str(), std::ios::out | std::ios::binary); 145 | fout << response.get(); 146 | } catch (std::exception& e) { 147 | // exceptions get propagated into awaiting coroutine 148 | 149 | printf ("HTTP download failed: %s\n", e.what()); 150 | } 151 | 152 | loo::mainLooper().schedule([] { 153 | loo::mainLooper().quit(); 154 | }); 155 | }); 156 | } 157 | 158 | void ex_awaitHttpClient() 159 | { 160 | // use a custom run loop 161 | loo::Looper mainLooper("main"); 162 | loo::setMainLooper(mainLooper); 163 | 164 | // Dispatch Boost.Asio ready handlers every 5ms. This is a simple way to integrate 165 | // Asio into your GUI without hogging CPU. Having Asio drive the run loop instead 166 | // may give better performance (see Flickr example). 167 | // 168 | mainLooper.scheduleRepeating([]() -> bool { 169 | if (sIo.stopped()) { 170 | sIo.reset(); 171 | } 172 | sIo.poll(); 173 | 174 | return true; 175 | }, 0, 5); 176 | 177 | ut::Awaitable awt = asyncHttpDownload("www.google.com", "/images/srpr/logo3w.png", "download.png"); 178 | 179 | mainLooper.run(); 180 | } 181 | -------------------------------------------------------------------------------- /Examples/ex_awaitThread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include "Looper/Thread.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | // 25 | // ABOUT: how to implement an Awaitable on top of threads, 26 | // how to handle interruption 27 | // 28 | 29 | // try to use std::thread, fallback to boost::thread 30 | // 31 | using namespace loo::lthread; 32 | using namespace loo::lchrono; 33 | 34 | 35 | // run loop 36 | static boost::asio::io_service sIo; 37 | 38 | 39 | static ut::Awaitable asyncCountdown() 40 | { 41 | return ut::startAsync("asyncCountdown", []() { 42 | timed_mutex mutex; 43 | condition_variable_any cond; 44 | bool isInterrupted = false; 45 | 46 | ut::Awaitable awtLiftoff("evt-liftoff"); 47 | 48 | thread countdownThread([&](ut::Completer completer) { 49 | unique_lock lock(mutex); 50 | 51 | for (int i = 3; i > 0 && !isInterrupted; i--) { 52 | printf ("%d seconds until liftoff...\n", i); 53 | 54 | // up to 1 second of interruptible sleep 55 | auto timepoint = chrono::steady_clock::now() + chrono::milliseconds(1000); 56 | cond.wait_until(lock, timepoint, [&] { return isInterrupted; }); 57 | } 58 | 59 | if (isInterrupted) { 60 | printf ("liftoff aborted!\n"); 61 | } else { 62 | printf ("liftoff!\n"); 63 | 64 | // Safe coroutine resumal: schedule completion on main thread. 65 | // 66 | // It's possible the abort comes too late to prevent liftoff. Completer checks 67 | // the awaitable is still valid, so nothing happens if it runs after thread.join(). 68 | // 69 | sIo.post(completer); 70 | } 71 | }, awtLiftoff.takeCompleter()); 72 | 73 | try { 74 | // suspend until liftoff or abort 75 | awtLiftoff.await(); 76 | } catch (const ut::ForcedUnwind&) { 77 | printf ("aborting liftoff...\n"); 78 | 79 | // launch aborted, interrupt countdown thread 80 | lock_guard _(mutex); 81 | isInterrupted = true; 82 | cond.notify_one(); 83 | } 84 | 85 | countdownThread.join(); 86 | printf ("\njoined countdown thread\n"); 87 | }); 88 | } 89 | 90 | static ut::Awaitable asyncKey() 91 | { 92 | return ut::startAsync("asyncKey", []() { 93 | ut::Coro *coro = ut::currentCoro(); 94 | 95 | thread keyThread([&]() { 96 | // Wait for user to hit [Return]. Uninterruptible blocking calls 97 | // are generally a bad idea. Here we pretend it's safe to kill 98 | // thread at any time. 99 | readLine(); 100 | 101 | sIo.post([&]() { 102 | // vulnerable to coro being destroyed in the meantime 103 | ut::yieldTo(coro); 104 | }); 105 | }); 106 | 107 | 108 | try { 109 | // ok -- yield explicitly to master context 110 | ut::yield(); 111 | 112 | keyThread.join(); 113 | printf ("\njoined key thread\n"); 114 | } catch (const ut::ForcedUnwind&) { 115 | keyThread.detach(); 116 | printf ("\nkilled key thread\n"); 117 | } 118 | }); 119 | } 120 | 121 | static ut::Awaitable asyncThread() 122 | { 123 | return ut::startAsync("asyncThread", []() { 124 | printf ("hit [Return] to abort launch\n\n"); 125 | 126 | { 127 | ut::Awaitable awtCountdown = asyncCountdown(); 128 | ut::Awaitable awtKey = asyncKey(); 129 | 130 | // wait until liftoff or abort 131 | ut::awaitAny(awtCountdown, awtKey); 132 | 133 | // scope end, the other awaitable will interrupt itself 134 | 135 | sIo.stop(); 136 | } 137 | }); 138 | } 139 | 140 | void ex_awaitThread() 141 | { 142 | ut::Awaitable awt = asyncThread(); 143 | 144 | // io_service::run() quits immediately if there's nothing scheduled 145 | 146 | // setup a dummy timer 147 | boost::asio::deadline_timer timer(sIo, boost::posix_time::hours(1)); 148 | timer.async_wait([](const boost::system::error_code& ec) { }); 149 | 150 | sIo.run(); 151 | } 152 | -------------------------------------------------------------------------------- /Examples/ex_comboDetector.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // 24 | // ABOUT: simulate FSM for detecting combos in a game 25 | // 26 | 27 | // valid combos 28 | // 29 | const std::array COMBOS = 30 | { { 31 | "xx", 32 | "yy", 33 | "xyx", 34 | "xyy", 35 | "yxx" 36 | } }; 37 | 38 | // input sequence 39 | // 40 | const std::string BUTTON_MASH = "xxy-xyyx-yxx"; 41 | 42 | // checks for combo patterns in input sequence 43 | // 44 | // input : char* -- button pushed 45 | // output : size_t* -- matched combo index, null if no match 46 | // 47 | static void coComboDetector(void *startValue) 48 | { 49 | std::list matches; 50 | 51 | auto resetMatches = [&]() { 52 | matches.clear(); 53 | for (size_t i = 0; i < COMBOS.size(); i++) { 54 | matches.push_back(i); 55 | } 56 | }; 57 | resetMatches(); 58 | 59 | size_t chrPos = 0; 60 | auto chr = (char *) startValue; 61 | 62 | while (chr != nullptr) { 63 | 64 | if (*chr == '-') { // start over 65 | resetMatches(); 66 | chrPos = 0; 67 | } else if (*chr == 'x' || *chr == 'y') { // filter matches 68 | for (auto it = matches.begin(); it != matches.end() ; ) { 69 | const std::string& combo = COMBOS[*it]; 70 | 71 | if (chrPos >= combo.size() || *chr != combo[chrPos]) { 72 | it = matches.erase(it); 73 | } else { 74 | ++it; 75 | } 76 | } 77 | chrPos++; 78 | } else { 79 | // ignore illegal input 80 | } 81 | 82 | if (matches.size() > 0 && COMBOS[matches.front()].size() == chrPos) { 83 | assert (matches.size() == 1); 84 | printf (" @ %ld, match\n", (long) chrPos); 85 | 86 | chr = (char *) ut::yield(&matches.front()); 87 | } else { 88 | printf (" @ %ld, %ld possible matches\n", (long) chrPos, (long) matches.size()); 89 | 90 | chr = (char *) ut::yield(); 91 | } 92 | }; 93 | } 94 | 95 | void ex_comboDetector() 96 | { 97 | auto detectorCoro = new ut::Coro("combo-detector", &coComboDetector); 98 | 99 | foreach_(char chr, BUTTON_MASH) { 100 | auto match = (size_t *) ut::yieldTo(detectorCoro, &chr); 101 | 102 | if (match != nullptr) { 103 | printf ("matched '%s'\n", COMBOS[*match].c_str()); 104 | } 105 | } 106 | 107 | ut::yieldTo(detectorCoro); // yield nullptr to quit 108 | delete detectorCoro; 109 | } 110 | -------------------------------------------------------------------------------- /Examples/ex_fibonacci.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include 19 | #include 20 | #include 21 | 22 | // 23 | // ABOUT: generator - lazy evaluation of an infinite sequence 24 | // 25 | 26 | static void coFiboGenerator(void *startValue) 27 | { 28 | (void) startValue; // unused 29 | 30 | long a = 0, b = 1; 31 | ut::yield(&a); 32 | ut::yield(&b); 33 | 34 | do { 35 | long temp = a; 36 | a = b; 37 | b += temp; 38 | 39 | ut::yield(&b); 40 | } while (true); 41 | } 42 | 43 | void ex_fibonacci() 44 | { 45 | // Explicit coroutine management 46 | { 47 | // Allocate a coroutine. Each coroutine has its own stack. 48 | ut::Coro fiboCoro("fibo-generator", &coFiboGenerator); 49 | 50 | for (int i = 0; i < 10; i++) { 51 | // Yield nullptr to coroutine 52 | auto value = (long *) ut::yieldTo(&fiboCoro); 53 | 54 | // Back from coroutine. Value points to an integer on fibo stack. 55 | printf ("%ld\n", *value); 56 | } 57 | 58 | // Terminate coroutine via exception. You could also yield a flag 59 | // that coroutine checks to see if it should quit. 60 | ut::forceUnwind(&fiboCoro); 61 | } 62 | 63 | printf("\n\n"); 64 | 65 | // Same output by letting YieldSequence handle the coroutine 66 | { 67 | ut::YieldSequence fibos(&coFiboGenerator); 68 | 69 | int i = 0; 70 | foreach_(long value, fibos) { 71 | printf ("%ld\n", value); 72 | 73 | if (++i == 10) 74 | break; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Examples/ex_iterator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include 19 | #include 20 | #include 21 | 22 | // 23 | // ABOUT: generator - iterates over a filtered collection 24 | // 25 | 26 | void ex_iterator() 27 | { 28 | std::array digits = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } }; 29 | 30 | auto coOddDigits = [&](void*) { 31 | foreach_(auto& value, digits) { 32 | if (value % 2 == 1) { 33 | ut::yield((void*)&value); 34 | } 35 | } 36 | 37 | // simply return to finish iteration 38 | }; 39 | 40 | ut::YieldSequence oddDigits(coOddDigits); 41 | 42 | foreach_(int value, oddDigits) { 43 | printf ("%d\n", value); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Examples/ex_stockClient.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // 18 | // ABOUT: Simple client to check stock prices. Helps compare the 3 approaches: 19 | // 1. blocking / 2. async await / 3. async callbacks 20 | 21 | #include "ExUtil.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | using boost::asio::ip::tcp; 29 | 30 | namespace ph = std::placeholders; 31 | 32 | typedef std::map StockMap; 33 | 34 | 35 | // ---------------------------------------------- 36 | // blocking version 37 | // ---------------------------------------------- 38 | 39 | static void fetchStocks_sync(const std::string& host, const std::string& port, StockMap& stocks) 40 | { 41 | boost::asio::io_service io; 42 | 43 | try { 44 | tcp::resolver resolver(io); 45 | tcp::resolver::query query(tcp::v4(), host, port); 46 | 47 | tcp::resolver::iterator itEndpoints = resolver.resolve(query); 48 | 49 | tcp::socket socket(io); 50 | boost::asio::connect(socket, itEndpoints); 51 | 52 | boost::asio::streambuf requestBuf; 53 | boost::asio::streambuf replyBuf; 54 | 55 | foreach_(auto& stock, stocks) { 56 | const std::string& symbol = stock.first; 57 | float& price = stock.second; 58 | 59 | // write symbol 60 | std::ostream os(&requestBuf); 61 | os << symbol << "\n"; 62 | boost::asio::write(socket, requestBuf); 63 | 64 | // read price 65 | boost::asio::read_until(socket, replyBuf, "\n"); 66 | std::istream is(&replyBuf); 67 | is >> price; 68 | is.ignore(1, '\n'); // consume newline 69 | 70 | printf ("stock %s : %.2f\n", symbol.c_str(), price); 71 | } 72 | 73 | // end session 74 | boost::asio::write(socket, boost::asio::buffer("\n")); 75 | } catch (const std::exception& e) { 76 | fprintf (stderr, "Failed: %s - %s\n", typeid(e).name(), e.what()); 77 | } 78 | } 79 | 80 | 81 | // ---------------------------------------------- 82 | // async await version 83 | // ---------------------------------------------- 84 | 85 | static void fetchStocks_asyncAwait(const std::string& host, const std::string& port, StockMap& stocks) 86 | { 87 | boost::asio::io_service io; 88 | 89 | ut::Awaitable awt = ut::startAsync("asyncFetchStocks", [&]() { 90 | try { 91 | ut::Awaitable awt; 92 | 93 | tcp::resolver resolver(io); 94 | tcp::resolver::query query(tcp::v4(), host, port); 95 | 96 | tcp::resolver::iterator itEndpoints; 97 | awt = ut::asio::asyncResolve(resolver, query, itEndpoints); 98 | awt.await(); 99 | 100 | tcp::socket socket(io); 101 | 102 | tcp::resolver::iterator itConnected; 103 | awt = ut::asio::asyncConnect(socket, itEndpoints, itConnected); 104 | awt.await(); 105 | 106 | auto requestBuf = std::make_shared(); 107 | auto replyBuf = std::make_shared(); 108 | 109 | foreach_(auto& stock, stocks) { 110 | const std::string& symbol = stock.first; 111 | float& price = stock.second; 112 | 113 | // write symbol 114 | std::ostream os(requestBuf.get()); 115 | os << symbol << "\n"; 116 | awt = ut::asio::asyncWrite(socket, requestBuf); 117 | awt.await(); 118 | 119 | // read price 120 | awt = ut::asio::asyncReadUntil(socket, replyBuf, std::string("\n")); 121 | awt.await(); 122 | std::istream is(replyBuf.get()); 123 | is >> price; 124 | is.ignore(1, '\n'); // consume newline 125 | 126 | printf ("stock %s : %.2f\n", symbol.c_str(), price); 127 | } 128 | 129 | // end session 130 | awt = ut::asio::asyncWrite(socket, std::make_shared("\n")); 131 | awt.await(); 132 | } catch (const std::exception& e) { 133 | fprintf (stderr, "Failed: %s - %s\n", typeid(e).name(), e.what()); 134 | } 135 | }); 136 | 137 | // run main loop; loops until all async handlers have ben dispatched 138 | io.run(); 139 | } 140 | 141 | 142 | // ---------------------------------------------- 143 | // async callbacks version 144 | // ---------------------------------------------- 145 | 146 | class StockClient 147 | { 148 | public: 149 | StockClient(boost::asio::io_service& io, const std::string& host, const std::string& port, StockMap& stocks) 150 | : mStocks(stocks) 151 | , mResolverQuery(tcp::v4(), host, port) 152 | , mResolver(io) 153 | , mSocket(io) 154 | { 155 | } 156 | 157 | void start() 158 | { 159 | mResolver.async_resolve(mResolverQuery, 160 | std::bind(&StockClient::handleResolved, this, ph::_1, ph::_2)); 161 | } 162 | 163 | private: 164 | void handleResolved(const boost::system::error_code& ec, tcp::resolver::iterator itEndpoints) 165 | { 166 | if (ec) { 167 | handleError(ec); 168 | return; 169 | } 170 | 171 | boost::asio::async_connect(mSocket, itEndpoints, 172 | std::bind(&StockClient::handleConnected, this, ph::_1, ph::_2)); 173 | } 174 | 175 | void handleConnected(const boost::system::error_code& ec, tcp::resolver::iterator /* itConnected */) 176 | { 177 | if (ec) { 178 | handleError(ec); 179 | return; 180 | } 181 | 182 | mPos = mStocks.begin(); 183 | doWriteSymbol(); 184 | } 185 | 186 | void handleWroteSymbol(const boost::system::error_code& ec, std::size_t /* bytesTransferred */) 187 | { 188 | if (ec) { 189 | handleError(ec); 190 | return; 191 | } 192 | 193 | if (mPos == mStocks.end()) { 194 | handleDone(); 195 | return; 196 | } 197 | 198 | doReadPrice(); 199 | } 200 | 201 | void handleReadPrice(const boost::system::error_code& ec, std::size_t /* bytesTransferred */) 202 | { 203 | const std::string& symbol = mPos->first; 204 | float& price = mPos->second; 205 | 206 | std::istream is(&mReplyBuf); 207 | is >> price; 208 | is.ignore(1, '\n'); // consume newline 209 | 210 | printf ("<-- stock %s : %.2f\n", symbol.c_str(), price); 211 | 212 | mPos++; 213 | doWriteSymbol(); 214 | 215 | } 216 | 217 | void handleError(const boost::system::error_code& ec) 218 | { 219 | fprintf (stderr, "Failed: %d - %s\n", ec.value(), ec.message().c_str()); 220 | } 221 | 222 | void handleDone() 223 | { 224 | } 225 | 226 | void doWriteSymbol() 227 | { 228 | std::string symbol; 229 | 230 | if (mPos == mStocks.end()) { 231 | // send empty line to end session 232 | } else { 233 | symbol = mPos->first; 234 | } 235 | 236 | std::ostream os(&mRequestBuf); 237 | os << symbol << "\n"; 238 | boost::asio::async_write(mSocket, mRequestBuf, 239 | std::bind(&StockClient::handleWroteSymbol, this, ph::_1, ph::_2)); 240 | } 241 | 242 | void doReadPrice() 243 | { 244 | boost::asio::async_read_until(mSocket, mReplyBuf, "\n", 245 | std::bind(&StockClient::handleReadPrice, this, ph::_1, ph::_2)); 246 | } 247 | 248 | StockMap& mStocks; 249 | tcp::resolver::query mResolverQuery; 250 | tcp::resolver mResolver; 251 | tcp::socket mSocket; 252 | 253 | StockMap::iterator mPos; 254 | boost::asio::streambuf mRequestBuf; 255 | boost::asio::streambuf mReplyBuf; 256 | }; 257 | 258 | static void fetchStocks_asyncCallbacks(const std::string& host, const std::string& port, StockMap& stocks) 259 | { 260 | boost::asio::io_service io; 261 | 262 | StockClient session(io, host, port, stocks); 263 | session.start(); 264 | 265 | // run main loop; loops until all async handlers have ben dispatched 266 | io.run(); 267 | } 268 | 269 | // ---------------------------------------------- 270 | 271 | void ex_stockClient() 272 | { 273 | const std::string host = "localhost"; 274 | const std::string port = "3455"; 275 | 276 | // stocks to query for 277 | std::map stocks; 278 | stocks["ARMH"] = 0; 279 | stocks["INTC"] = 0; 280 | stocks["TXN"] = 0; 281 | 282 | size_t selected = 0; 283 | 284 | while (selected < 1 || 3 < selected) { 285 | printf ("Select version:\n"); 286 | printf ("1. blocking\n"); 287 | printf ("2. async await\n"); 288 | printf ("3. async callbacks\n"); 289 | printf ("\n"); 290 | printf ("> "); 291 | 292 | try { 293 | const char* line = readLine(); 294 | selected = boost::lexical_cast(line); 295 | } catch (...) { 296 | } 297 | printf ("\n"); 298 | } 299 | 300 | if (selected == 1) { 301 | fetchStocks_sync(host, port, stocks); 302 | } else if (selected == 2) { 303 | fetchStocks_asyncAwait(host, port, stocks); 304 | } else if (selected == 3) { 305 | fetchStocks_asyncCallbacks(host, port, stocks); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /Examples/ex_stockServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // 18 | // ABOUT: Fake stock server. Uses blocking API to keep it simple, can 19 | // only deal with one client at a time. For an asynchronous server 20 | // see ex_awaitChatServer.cpp. 21 | // 22 | 23 | #include "ExUtil.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | using boost::asio::ip::tcp; 31 | 32 | typedef std::map StockMap; 33 | 34 | void ex_stockServer() 35 | { 36 | // dummy stock prices 37 | std::map stocks; 38 | stocks["INTC"] = 22.39f; 39 | stocks["AMD"] = 2.61f; 40 | stocks["NVDA"] = 12.70f; 41 | stocks["ARMH"] = 40.70f; 42 | stocks["TXN"] = 35.75f; 43 | 44 | boost::asio::io_service io; 45 | 46 | tcp::endpoint endpoint(tcp::v4(), 3455); 47 | 48 | tcp::acceptor acceptor(io, endpoint.protocol()); 49 | try { 50 | acceptor.bind(endpoint); 51 | acceptor.listen(); 52 | } catch (...) { 53 | fprintf (stderr, "Couldn't bind to port %u!\n", endpoint.port()); 54 | return; 55 | } 56 | 57 | while (true) { 58 | printf ("waiting for new client...\n"); 59 | 60 | tcp::socket socket(io); 61 | 62 | try { 63 | acceptor.accept(socket); 64 | 65 | bool quit = false; 66 | 67 | boost::asio::streambuf requestBuf; 68 | boost::asio::streambuf replyBuf; 69 | 70 | // for each stock symbol read, reply with stock price. Client 71 | // should send an empty line before disconnecting. 72 | // 73 | do { 74 | std::string symbol; 75 | 76 | boost::asio::read_until(socket, requestBuf, "\n"); 77 | std::istream is(&requestBuf); 78 | is >> symbol; 79 | is.ignore(1, '\n'); // consume newline 80 | 81 | if (symbol.size() >= 3) { 82 | float price = 0.0f; 83 | StockMap::iterator pos = stocks.find(symbol); 84 | if (pos != stocks.end()) { 85 | price = pos->second; 86 | } 87 | 88 | printf ("--> stock %s : %.2f\n", symbol.c_str(), price); 89 | 90 | std::ostream os(&replyBuf); 91 | os << price << "\n"; 92 | boost::asio::write(socket, replyBuf); 93 | } else { 94 | printf ("client session finished\n"); 95 | quit = true; 96 | } 97 | } while (!quit); 98 | } catch (const std::exception& e) { 99 | fprintf (stderr, "Session error: %s - %s\n", typeid(e).name(), e.what()); 100 | } 101 | } 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /Examples/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "ExUtil.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | void ex_fibonacci(); 24 | void ex_iterator(); 25 | void ex_comboDetector(); 26 | void ex_awaitBasics(); 27 | void ex_awaitThread(); 28 | void ex_awaitHttpClient(); 29 | void ex_awaitFlickr(); 30 | void ex_awaitChatServer(); 31 | void ex_awaitChatClient(); 32 | void ex_stockServer(); 33 | void ex_stockClient(); 34 | 35 | 36 | struct Example 37 | { 38 | void (*function)(); 39 | const char *description; 40 | }; 41 | 42 | static const Example EXAMPLES[] = 43 | { 44 | { &ex_fibonacci, "coroutines - fibonacci sequence generator" }, 45 | { &ex_iterator, "coroutines - collection iterator" }, 46 | { &ex_comboDetector, "coroutines - combo detector" }, 47 | { &ex_awaitBasics, "await - basics" }, 48 | { &ex_awaitThread, "await - threads example" }, 49 | { &ex_awaitHttpClient, "await - HTTP client" }, 50 | #ifdef HAVE_OPENSSL 51 | { &ex_awaitFlickr, "await - Flickr client" }, 52 | #endif 53 | { &ex_awaitChatServer, "await - chat server" }, 54 | { &ex_awaitChatClient, "await - chat client" }, 55 | { &ex_stockServer, "stock price server" }, 56 | { &ex_stockClient, "stock price client" }, 57 | }; 58 | 59 | int main(int argc, char** argv) 60 | { 61 | // ut::setLogLevel(ut::LOGLEVEL_DEBUG); 62 | 63 | size_t selected = 0; 64 | 65 | if (argc == 2) { 66 | selected = boost::lexical_cast(argv[1]); 67 | } 68 | 69 | const int numExamples = sizeof(EXAMPLES) / sizeof(Example); 70 | 71 | while (selected < 1 || numExamples < selected) { 72 | printf ("Examples:\n\n"); 73 | 74 | for (size_t i = 0; i < numExamples; i++) { 75 | printf ("%02d: %s\n", (int) (i + 1), EXAMPLES[i].description); 76 | } 77 | 78 | printf ("\nSelect: "); 79 | try { 80 | const char* line = readLine(); 81 | selected = boost::lexical_cast(line); 82 | } catch (...) { 83 | } 84 | 85 | printf ("\n---------\n\n"); 86 | } 87 | 88 | auto& example = EXAMPLES[selected - 1]; 89 | example.function(); 90 | 91 | printf ("\nDONE\n"); 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /Examples/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" -------------------------------------------------------------------------------- /Examples/stdafx.h: -------------------------------------------------------------------------------- 1 | #include "ExUtil.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /PROJECTS: -------------------------------------------------------------------------------- 1 | CppAwait 2 | -------- 3 | main library 4 | 5 | 6 | Examples 7 | -------- 8 | examples for using coroutines & awaitables 9 | 10 | 11 | Examples/Looper 12 | --------- 13 | support library for examples, implements a run loop 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NOTICE 2 | 3 | ### **This library is no longer being maintained. All development has shifted to [CppAsync](https://github.com/vmilea/CppAsync), a complete reimplementation that is more efficient, flexible, and doesn't depend on Boost.Context.** 4 | 5 | CppAwait 6 | ======== 7 | 8 | CppAwait is a C++ library that enables writing asynchronous code in a natural (sequential) form. 9 | 10 | It solves the code flow inversion typical of asynchronous APIs. So -- instead of chaining callbacks, dealing with state and error codes -- you can use plain conditionals, loops and exception handlers to express your asynchronous algorithm. 11 | 12 | The goal: make it easier to write responsive applications that have to deal with laggy network & file I/O. 13 | 14 | 15 | How it works 16 | ============ 17 | 18 | CppAwait provides an _await_ construct similar to the one from C# 5 (see [MSDN](http://msdn.microsoft.com/en-us/library/hh191443.aspx)). Upon reaching an _await_ expression the C# compiler automatically transforms the rest of the method into a task continuation. In C++ a similar effect can be achieved through coroutines (which CppAwait implements on top of Boost.Context). Calling `await()` will suspend a coroutine until the associated task completes. The program is free to do other work while coroutine is suspended. 19 | 20 | Your program needs a run loop to manage asynchronous operations. 21 | 22 | 23 | Here is a snippet showing typical library use. It connects and transfers some data to a TCP server: 24 | 25 | ut::Awaitable awt; 26 | 27 | tcp::resolver::iterator outEndpoints; 28 | awt = ut::asio::asyncResolve(resolver, query, outEndpoints); 29 | // Suspend coroutine until task has finished 30 | awt.await(); 31 | 32 | for (auto it = outEndpoints; it != tcp::resolver::iterator(); ++it) { 33 | tcp::endpoint ep = *it; 34 | awt = ut::asio::asyncConnect(socket, ep); 35 | 36 | try { 37 | awt.await(); 38 | break; // connected 39 | } catch (const std::exception&) { 40 | // Try next endpoint 41 | } 42 | } 43 | if (!socket.is_open()) { 44 | throw SocketError("failed to connect socket"); 45 | } 46 | 47 | // Write an HTTP request 48 | awt = ut::asio::asyncWrite(socket, request); 49 | awt.await(); 50 | 51 | // Read response 52 | awt = ut::asio::asyncReadUntil(socket, outResponse, std::string("\r\n")); 53 | awt.await(); 54 | 55 | The `Awaitable` class is a generic wrapper for asynchronous operations. All asynchronous methods return an `Awaitable`. Its `await()` method yields control to the main loop until the `Awaitable` completes or fails, at which point the coroutine resumes. If operation failed the associated exception gets thrown. Note there is no return value -- output parameters such as _endpoints_ must be passed by reference. 56 | 57 | `Awaitables` compose easily, just like regular functions. More complex patterns are supported through `awaitAll()` / `awaitAny()`. 58 | 59 | 60 | Here is another snippet which downloads and archives some files. While a file is being archived, the next one is being downloaded: 61 | 62 | std::vector urls = { ... }; 63 | 64 | ut::Awaitable awtArchive; 65 | 66 | for (std::string url : urls) { 67 | // Holds fetched document 68 | std::unique_ptr > document; 69 | 70 | ut::Awaitable awtFetch = asyncFetch(url, &document); 71 | 72 | // Doesn't block, instead it yields. The coroutine 73 | // gets resumed when fetch done or on exception. 74 | awtFetch.await(); 75 | 76 | if (!awtArchive.isNil()) { 77 | awtArchive.await(); 78 | } 79 | awtArchive = asyncArchive(std::move(document)); 80 | } 81 | 82 | 83 | There are several [examples](/Examples) included. See [stock client](/Examples/ex_stockClient.cpp) for a direct comparison between classic async and the await pattern. 84 | 85 | 86 | Features 87 | ======== 88 | 89 | - coroutines, iterable generators 90 | 91 | - composable awaitables 92 | 93 | - support for exceptions 94 | 95 | - can adapt any asynchronous API 96 | 97 | - _Boost.Asio_ wrappers 98 | 99 | - pluggable into any program with a run loop 100 | 101 | - good performance with stack pooling 102 | 103 | - customizable logging 104 | 105 | - portable 106 | 107 | 108 | Installation 109 | ============ 110 | 111 | 1. __Install [Boost](http://www.boost.org/users/download/).__ To use CppAwait you need Boost 1.58+ with _Boost.Context_, _Boost.Thread_ and _Boost.System_ compiled [[*]](#deps). Quick guide: 112 | 113 | - download Boost archive 114 | - `./bootstrap` 115 | - `./b2 link=static --build-type=minimal --with-context --with-chrono --with-thread --toolset=your-toolset stage` 116 | 117 | 2. __Install [CMake](http://www.cmake.org/cmake/resources/software.html) 2.8+__ 118 | 119 | 3. __Build CppAwait__: 120 | 121 | - `mkdir build_dir ; cd build_dir` 122 | - `cmake -G "your-generator" -DBOOST_ROOT="path-to-boost" "path-to-CppAwait"` 123 | - open solution / make 124 | 125 | (*) _Boost.Chrono_ is additionally required to compile the examples on Visual C++ 2010. There is an optional Flickr example which depends on OpenSSL. Place OpenSSL under `C:\OpenSSL` for automatic detection on Windows. 126 | 127 | 128 | Portability 129 | =========== 130 | 131 | The library is supported on Windows / Linux with any of these compilers: 132 | 133 | - Visual C++ 2010 or later 134 | - GCC 4.6.2 or later 135 | 136 | _Boost.Context_ includes assembly code for ARM / MIPS / PowerPC32 / PowerPC64 / X86-32 / X86-64. 137 | 138 | Porting to additional platforms (e.g. iOS, Android) should be trivial. 139 | 140 | 141 | 142 | Authors 143 | ======= 144 | 145 | Valentin Milea 146 | 147 | 148 | License 149 | ======= 150 | 151 | Copyright 2012-2015 Valentin Milea 152 | 153 | Licensed under the Apache License, Version 2.0 (the "License"); 154 | you may not use this file except in compliance with the License. 155 | You may obtain a copy of the License at 156 | 157 | http://www.apache.org/licenses/LICENSE-2.0 158 | 159 | Unless required by applicable law or agreed to in writing, software 160 | distributed under the License is distributed on an "AS IS" BASIS, 161 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 162 | See the License for the specific language governing permissions and 163 | limitations under the License. -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * more documentation 2 | * more/better examples 3 | * replace some asserts with exceptions 4 | * pattern based await 5 | -------------------------------------------------------------------------------- /cmake_modules/enable_max_warning_level.cmake: -------------------------------------------------------------------------------- 1 | # Enables highest warning level for MSVC / GCC 2 | 3 | if (MSVC) 4 | # MSVC complains if a different warning level is appended 5 | if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 6 | string (REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 7 | else() 8 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 9 | endif() 10 | elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) 11 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") 12 | endif() -------------------------------------------------------------------------------- /cmake_modules/precompiled_header.cmake: -------------------------------------------------------------------------------- 1 | # Generic support for precompiled headers 2 | # 3 | # Needs CMAKE_CURRENT_BINARY_DIR added to include path, 4 | # this can be done with set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 5 | 6 | function (ADD_PRECOMPILED_HEADER _headerFilename _srcList) 7 | 8 | if (CMAKE_COMPILER_IS_GNUCXX) 9 | string (TOUPPER "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" _cxxFlagsVarName) 10 | set (_args ${${_cxxFlagsVarName}}) 11 | 12 | get_directory_property (_definitions DEFINITIONS) 13 | list (APPEND _args ${_definitions}) 14 | 15 | get_directory_property (includeDirs INCLUDE_DIRECTORIES) 16 | foreach (includeDir ${includeDirs}) 17 | list (APPEND _args "-I${includeDir}") 18 | endforeach() 19 | 20 | list (APPEND _args -x c++-header -c ${CMAKE_CURRENT_SOURCE_DIR}/${_headerFilename} -o ${_headerFilename}.gch) 21 | 22 | separate_arguments (_args) 23 | 24 | add_custom_command (OUTPUT ${_headerFilename}.gch 25 | COMMAND ${CMAKE_CXX_COMPILER} ${_args} 26 | DEPENDS ${_headerFilename}) 27 | 28 | set_source_files_properties (${${_srcList}} PROPERTIES 29 | COMPILE_FLAGS "-include ${_headerFilename} -Winvalid-pch" 30 | OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_headerFilename}.gch) 31 | 32 | elseif (MSVC) 33 | get_filename_component (_headerBasename ${_headerFilename} NAME_WE) 34 | 35 | set (pchFilename "${CMAKE_CURRENT_BINARY_DIR}/${_headerBasename}.pch") 36 | 37 | foreach (src ${${_srcList}}) 38 | get_filename_component (_srcBasename ${src} NAME_WE) 39 | 40 | if (_srcBasename STREQUAL _headerBasename) 41 | set_source_files_properties (${src} PROPERTIES 42 | COMPILE_FLAGS "/Yc\"${_headerFilename}\" /Fp\"${pchFilename}\"" 43 | OBJECT_OUTPUTS ${pchFilename}) 44 | else() 45 | set_source_files_properties (${src} PROPERTIES 46 | COMPILE_FLAGS "/Yu\"${_headerFilename}\" /FI\"${_headerFilename}\" /Fp\"${pchFilename}\"" 47 | OBJECT_DEPENDS ${pchFilename}) 48 | endif() 49 | endforeach() 50 | endif() 51 | 52 | endfunction() -------------------------------------------------------------------------------- /include/CppAwait/BoundedQueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file BoundedQueue.h 19 | * 20 | * Declares the BoundedQueue class. 21 | * 22 | */ 23 | 24 | #pragma once 25 | 26 | #include "Config.h" 27 | #include "Condition.h" 28 | #include "impl/Assert.h" 29 | #include 30 | 31 | namespace ut { 32 | 33 | /** 34 | * Asynchronous bounded queue 35 | * 36 | * BoundedQueue supports async producer-consumer patterns. 37 | * 38 | * @warning Not thread safe. BoundedQueues are designed for single-threaded use. 39 | */ 40 | template 41 | struct BoundedQueue 42 | { 43 | public: 44 | /** Construct a queue that can grow up to maxSize */ 45 | BoundedQueue(size_t maxSize = (size_t) -1) 46 | : mMaxSize(maxSize) { } 47 | 48 | /** Max queue size */ 49 | size_t maxSize() const 50 | { 51 | return mMaxSize; 52 | } 53 | 54 | /** Queue size */ 55 | size_t size() const 56 | { 57 | return mQueue.size(); 58 | } 59 | 60 | /** Check if queue empty */ 61 | bool isEmpty() const 62 | { 63 | return mQueue.empty(); 64 | } 65 | 66 | /** Check if queue full */ 67 | bool isFull() const 68 | { 69 | return size() == maxSize(); 70 | } 71 | 72 | /** 73 | * Push a value. Push is performed immediately unless queue full. 74 | * 75 | * @param value value to push 76 | * @return an awaitable that completes after value has been pushed. 77 | */ 78 | Awaitable asyncPush(T value) 79 | { 80 | if (mQueue.size() < mMaxSize) { 81 | mQueue.push_back(std::move(value)); 82 | mCondPoppable.notifyOne(); 83 | 84 | return Awaitable::makeCompleted(); 85 | } else { 86 | Awaitable awt = mCondPushable.asyncWait(); 87 | auto awtPtr = awt.pointer(); 88 | 89 | awt.then([this, awtPtr, value]() { 90 | if (!awtPtr->didFail()) { 91 | ut_assert_(this->mQueue.size() < this->mMaxSize); 92 | 93 | this->mQueue.push_back(value); 94 | this->mCondPoppable.notifyOne(); 95 | } 96 | }); 97 | 98 | return std::move(awt); 99 | } 100 | } 101 | 102 | /** 103 | * Pop a value. Pop is performed immediately unless queue empty. 104 | * 105 | * @param outValue holds value, must not be freed before awaitable done 106 | * @return an awaitable that completes after value has been popped. 107 | */ 108 | Awaitable asyncPop(T& outValue) 109 | { 110 | if (!mQueue.empty()) { 111 | outValue = std::move(mQueue.front()); 112 | mQueue.pop_front(); 113 | mCondPushable.notifyOne(); 114 | 115 | return Awaitable::makeCompleted(); 116 | } else { 117 | Awaitable awt = mCondPoppable.asyncWait(); 118 | auto awtPtr = awt.pointer(); 119 | 120 | awt.then([this, awtPtr, &outValue]() { 121 | if (!awtPtr->didFail()) { 122 | ut_assert_(!this->mQueue.empty()); 123 | 124 | outValue = std::move(this->mQueue.front()); 125 | this->mQueue.pop_front(); 126 | this->mCondPushable.notifyOne(); 127 | } 128 | }); 129 | 130 | return std::move(awt); 131 | } 132 | } 133 | 134 | private: 135 | BoundedQueue(const BoundedQueue&); // noncopyable 136 | BoundedQueue& operator=(const BoundedQueue&); // noncopyable 137 | 138 | size_t mMaxSize; 139 | std::deque mQueue; 140 | 141 | Condition mCondPoppable; // not empty 142 | Condition mCondPushable; // not full 143 | }; 144 | 145 | } -------------------------------------------------------------------------------- /include/CppAwait/Condition.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file Condition.h 19 | * 20 | * Declares the Condition class. 21 | * 22 | */ 23 | 24 | #pragma once 25 | 26 | #include "Config.h" 27 | #include "Awaitable.h" 28 | #include "impl/Assert.h" 29 | #include 30 | 31 | namespace ut { 32 | 33 | /** 34 | * Condition variable 35 | * 36 | * This is an equivalent of multithreading condition variables. Instead of blocking, 37 | * you asynchronously wait for condition to get triggered. 38 | * 39 | * Several coroutines may be waiting for the condition at the same time. It's possible 40 | * to notify one or all of them. 41 | * 42 | * Notes: 43 | * - It's fine to immediately call asyncWait() again from awoken coroutine. To avoid 44 | * infinite loops, awaitables added during notification are not awaken. 45 | * - You may call notify recursively (i.e. notify from awoken coroutine) 46 | * - There are no spurious wakeups 47 | * 48 | * @warning Not thread safe. Conditions are designed for single-threaded use. 49 | * 50 | */ 51 | class Condition 52 | { 53 | public: 54 | /** Construct a condition */ 55 | Condition(std::string tag = std::string()) 56 | : mTag(tag) 57 | , mLastWaiterId(0) { } 58 | 59 | /** Identifier for debugging */ 60 | const char* tag() 61 | { 62 | return mTag.c_str(); 63 | } 64 | 65 | /** Sets an identifier for debugging */ 66 | void setTag(std::string tag) 67 | { 68 | mTag = std::move(tag); 69 | } 70 | 71 | /** Wait until condition triggered */ 72 | Awaitable asyncWait() 73 | { 74 | Awaitable awt; 75 | 76 | mWaiters.push_back( 77 | Waiter(++mLastWaiterId, awt.takeCompleter())); 78 | 79 | return std::move(awt); 80 | } 81 | 82 | /** 83 | * Triggers condition, completes a single async wait 84 | * 85 | * The first Awaitable gets completed. 86 | */ 87 | void notifyOne() 88 | { 89 | if (mWaiters.empty()) { 90 | return; 91 | } 92 | 93 | { ut::PushMasterCoro _; 94 | do { 95 | Completer completer = std::move(mWaiters.front().completer); 96 | mWaiters.pop_front(); 97 | 98 | if (!completer.isExpired()) { 99 | completer(); 100 | break; 101 | } 102 | } while (!mWaiters.empty()); 103 | } 104 | } 105 | 106 | /** 107 | * Triggers condition, completes all async waits 108 | * 109 | * Awaitables get completed in FIFO order. 110 | */ 111 | void notifyAll() 112 | { 113 | if (mWaiters.empty()) { 114 | return; 115 | } 116 | 117 | { ut::PushMasterCoro _; 118 | Waiter::Id maxId = mLastWaiterId; 119 | 120 | ut_assert_(mWaiters.back().id == maxId); 121 | ut_assert_(mWaiters.front().id <= maxId); 122 | 123 | do { 124 | Completer completer = std::move(mWaiters.front().completer); 125 | mWaiters.pop_front(); 126 | 127 | completer(); 128 | } while (!mWaiters.empty() && mWaiters.front().id <= maxId); 129 | } 130 | } 131 | 132 | private: 133 | struct Waiter 134 | { 135 | typedef size_t Id; 136 | 137 | Id id; 138 | Completer completer; 139 | 140 | Waiter(Id id, Completer&& completer) 141 | : id(id) 142 | , completer(std::move(completer)) { } 143 | 144 | Waiter(Waiter&& other) 145 | : id(std::move(other.id)) 146 | , completer(std::move(other.completer)) 147 | { 148 | } 149 | 150 | Waiter& operator=(Waiter&& other) 151 | { 152 | id = other.id; 153 | completer = std::move(other.completer); 154 | 155 | return *this; 156 | } 157 | 158 | private: 159 | Waiter(const Waiter& other); // noncopyable 160 | Waiter& operator=(const Waiter& other); // noncopyable 161 | }; 162 | 163 | Condition(const Condition&); // noncopyable 164 | Condition& operator=(const Condition&); // noncopyable 165 | 166 | std::string mTag; 167 | 168 | Waiter::Id mLastWaiterId; 169 | std::deque mWaiters; 170 | 171 | }; 172 | 173 | } 174 | -------------------------------------------------------------------------------- /include/CppAwait/Config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #ifndef WINVER 23 | #define WINVER 0x501 24 | #endif 25 | 26 | #ifndef _WIN32_WINNT 27 | #define _WIN32_WINNT WINVER 28 | #endif 29 | 30 | #ifdef _MSC_VER 31 | // workaround for Boost.Asio: WinSock.h has already been included 32 | #define WIN32_LEAN_AND_MEAN 33 | #endif 34 | 35 | /// 36 | 37 | #define ut_concatenate_(s1, s2) s1##s2 38 | #define ut_concatenate_indirect_(s1, s2) ut_concatenate_(s1, s2) 39 | #define ut_anonymous_variable_(str) ut_concatenate_indirect_(str, __LINE__) 40 | 41 | /// 42 | 43 | #ifdef _MSC_VER 44 | 45 | #define ut_multi_line_macro_begin_ \ 46 | do { \ 47 | __pragma(warning(push)) \ 48 | __pragma(warning(disable:4127)) 49 | 50 | #define ut_multi_line_macro_end_ \ 51 | } while(0) \ 52 | __pragma(warning(pop)) 53 | 54 | #else // not _MSC_VER 55 | 56 | #define ut_multi_line_macro_begin_ do { 57 | #define ut_multi_line_macro_end_ } while(0) 58 | 59 | #endif // _MSC_VER 60 | 61 | /// 62 | 63 | #ifndef va_copy 64 | # ifdef __va_copy 65 | # define va_copy(a,b) __va_copy(a,b) 66 | # else 67 | # define va_copy(a, b) memcpy(&(a), &(b), sizeof(a)) 68 | # endif 69 | #endif 70 | 71 | /// 72 | 73 | #ifdef __cplusplus 74 | 75 | #include 76 | 77 | #endif // __cplusplus 78 | -------------------------------------------------------------------------------- /include/CppAwait/Coro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file Coro.h 19 | * 20 | * Declares the Coro class and various helper functions. 21 | * 22 | */ 23 | 24 | #pragma once 25 | 26 | #include "Config.h" 27 | #include "misc/Functional.h" 28 | #include "impl/Compatibility.h" 29 | #include 30 | #include 31 | 32 | /** CppAwait namespace */ 33 | namespace ut { 34 | 35 | /** 36 | * Basic coroutine abstraction 37 | * 38 | * Coro supports stackful coroutines on top of Boost.Context. 39 | * 40 | * Coroutines are a form of cooperative multitasking. At various points during its execution 41 | * a coroutine may decide to yield control to another. The yielding coroutine is suspended with 42 | * its stack intact and a snapshot of cpu registers, until some other coroutine yields back to it. 43 | * 44 | * The library will associate a default Coro with main program stack. Additional coroutines can 45 | * be started. Each new Coro remembers its parent and will yield to it when done. Note, in general 46 | * there are no restrictions -- a coroutine may yield to any other at any time. 47 | * 48 | * It is possible to yield both regular values and exceptions. To keep thing simple values are 49 | * passed as raw pointers. It's safe to access local variables from inside the coroutine while 50 | * it is suspended. 51 | * 52 | * Exception are passed as std::exception_ptr and get thrown on receiving end. There is no limit 53 | * on the number of exceptions yielded as long as they don't cause coroutine to finish. Uncaught 54 | * exceptions pop back in parent coroutine (with the exception of ForcedUnwind which is silently 55 | * swallowed). 56 | * 57 | * Stack size can be configured per Coro. Complex coroutines may need extra space. Also consider 58 | * adjusting the default stack size, which is platform dependent. Note that actual stack usage 59 | * varies -- debug builds usually need larger stacks. 60 | * 61 | * Coros can be tagged to ease debugging. 62 | * 63 | * @warning Not thread safe. Coroutines are designed for single-threaded use. 64 | * 65 | */ 66 | class Coro 67 | { 68 | public: 69 | /** 70 | * Coroutine body signature 71 | * 72 | * Any uncaught exception will pop out on parent coroutine, 73 | * except ForcedUnwind which is silently swallowed. 74 | * 75 | * @param value initial value yielded to coroutine 76 | */ 77 | typedef std::function Func; 78 | 79 | /** Minimum stack size allowed on current platform */ 80 | static size_t minimumStackSize(); 81 | 82 | /** Maximum stack size allowed on current platform */ 83 | static size_t maximumStackSize(); 84 | 85 | /** Default stack size on current platform */ 86 | static size_t defaultStackSize(); 87 | 88 | /** Change default stack size for new stacks */ 89 | static void setDefaultStackSize(size_t size); 90 | 91 | /** Discard cached stack buffers */ 92 | static void drainStackPool(); 93 | 94 | /** 95 | * Create and initialize a coroutine 96 | * @param tag identifier for debugging 97 | * @param func coroutine body, may yield() 98 | * @param stackSize size of stack 99 | */ 100 | Coro(std::string tag, Func func, size_t stackSize = defaultStackSize()); 101 | 102 | /** 103 | * Create a coroutine 104 | * @param tag identifier for debugging 105 | * @param stackSize size of stack 106 | */ 107 | Coro(std::string tag, size_t stackSize = defaultStackSize()); 108 | 109 | /** Destroy coroutine. It is illegal to call the destructor of a running coroutine */ 110 | ~Coro(); 111 | 112 | /** Move contructor */ 113 | Coro(Coro&& other); 114 | 115 | /** Move assignment */ 116 | Coro& operator=(Coro&& other); 117 | 118 | /** Identifier for debugging */ 119 | const char* tag(); 120 | 121 | /** Returns true after init() until func returns */ 122 | bool isRunning(); 123 | 124 | /** Initialize coroutine. Note, func is not entered until resumed via yield() */ 125 | void init(Func func); 126 | 127 | /** 128 | * Suspend self, return value to parent coroutine 129 | * @param value data to yield 130 | * @return a value or exception 131 | */ 132 | void* yield(void *value = nullptr); 133 | 134 | /** 135 | * Suspend self, return value to given coroutine 136 | * @param resumeCoro coroutine to resume 137 | * @param value data to yield 138 | * @return a value or exception 139 | */ 140 | void* yieldTo(Coro *resumeCoro, void *value = nullptr); 141 | 142 | /** 143 | * Suspend self, throw exception on parent coroutine 144 | * @param eptr exception to throw on parent coroutine 145 | * @return a value or exception 146 | */ 147 | void* yieldException(std::exception_ptr eptr); 148 | 149 | /** 150 | * Suspend self, throw exception on given coroutine 151 | * @param resumeCoro coroutine to resume 152 | * @param eptr exception to throw on coroutine 153 | * @return a value or exception 154 | */ 155 | void* yieldExceptionTo(Coro *resumeCoro, std::exception_ptr eptr); 156 | 157 | /** 158 | * Suspend self, throw exception on parent coroutine. The current coroutine is 159 | * assumed to have finished unwinding, it will never be resumed and its stack 160 | * is being recycled. The exception_ptr must be allocated on heap and will be 161 | * automatically deleted before throwing the exception. 162 | * @param peptr heap allocated exception_ptr containing the exception to throw 163 | * @return a value or exception 164 | */ 165 | void* yieldFinalException(std::exception_ptr* peptr); 166 | 167 | /** 168 | * Suspend self, throw exception on given coroutine. The current coroutine is 169 | * assumed to have finished unwinding, it will never be resumed and its stack 170 | * is being recycled. The exception_ptr must be allocated on heap and will be 171 | * automatically deleted before throwing the exception. 172 | * @param resumeCoro coroutine to resume 173 | * @param peptr heap allocated exception_ptr containing the exception to throw 174 | * @return a value or exception 175 | */ 176 | void* yieldFinalExceptionTo(Coro *resumeCoro, std::exception_ptr* peptr); 177 | 178 | /** Coroutine to yield to by default */ 179 | Coro* parent(); 180 | 181 | /** Set parent coroutine */ 182 | void setParent(Coro *coro); 183 | 184 | private: 185 | enum YieldType 186 | { 187 | YT_RESULT, 188 | YT_EXCEPTION 189 | }; 190 | 191 | struct YieldValue 192 | { 193 | YieldType type; 194 | void *value; 195 | 196 | YieldValue(YieldType type, void *value) 197 | : type(type), value(value) { } 198 | }; 199 | 200 | static size_t sDefaultStackSize; 201 | 202 | static void fcontextFunc(intptr_t data); 203 | 204 | Coro(); 205 | Coro(const Coro& other); // noncopyable 206 | Coro& operator=(const Coro& other); // noncopyable 207 | 208 | void clear(); 209 | 210 | void* implYieldTo(Coro *resumeCoro, YieldType type, void *value); 211 | void* unpackYieldValue(const YieldValue& yReceived); 212 | 213 | struct Impl; 214 | Impl *m; 215 | 216 | friend void initCoroLib(); 217 | }; 218 | 219 | 220 | // 221 | // master/current coroutine 222 | // 223 | 224 | /** Initialize coroutine library. Must be called once from main stack. */ 225 | void initCoroLib(); 226 | 227 | /** 228 | * Returns the main coroutine. This points to program's default stack 229 | * and is available after initCoroLib(). 230 | */ 231 | Coro* mainCoro(); 232 | 233 | /** Returns the current coroutine */ 234 | Coro* currentCoro(); 235 | 236 | /** Returns the master coroutine */ 237 | Coro* masterCoro(); 238 | 239 | /** Temporarily makes current coroutine the master */ 240 | class PushMasterCoro 241 | { 242 | public: 243 | PushMasterCoro(); 244 | ~PushMasterCoro(); 245 | 246 | private: 247 | PushMasterCoro(const PushMasterCoro& other); // noncopyable 248 | PushMasterCoro& operator=(const PushMasterCoro& other); // noncopyable 249 | 250 | Coro *mPushedCoro; 251 | }; 252 | 253 | // 254 | // yield helpers 255 | // 256 | 257 | /** Helper function, yields from current coroutine */ 258 | inline void* yield(void *value = nullptr) 259 | { 260 | void *received = currentCoro()->yield(value); 261 | return received; 262 | } 263 | 264 | /** Helper function, yields from current coroutine */ 265 | inline void* yieldTo(Coro *resumeCoro, void *value = nullptr) 266 | { 267 | void *received = currentCoro()->yieldTo(resumeCoro, value); 268 | return received; 269 | } 270 | 271 | /** Helper function, yields from current coroutine */ 272 | inline void* yieldException(std::exception_ptr eptr) 273 | { 274 | void *received = currentCoro()->yieldException(std::move(eptr)); 275 | return received; 276 | } 277 | 278 | /** Helper function, yields from current coroutine */ 279 | inline void* yieldExceptionTo(Coro *resumeCoro, std::exception_ptr eptr) 280 | { 281 | void *received = currentCoro()->yieldExceptionTo(resumeCoro, std::move(eptr)); 282 | return received; 283 | } 284 | 285 | /** 286 | * Helper function, packs an exception and yields from current coroutine 287 | * 288 | * @warning Don't call this while an exception is propagating 289 | * as it relies on a buggy make_exception_ptr 290 | */ 291 | template 292 | void* yieldException(const T& e) 293 | { 294 | void *received = currentCoro()->yieldException(ut::make_exception_ptr(e)); 295 | return received; 296 | } 297 | 298 | /** 299 | * Helper function, packs an exception and yields from current coroutine 300 | * 301 | * @warning Don't call this while an exception is propagating 302 | * as it relies on a buggy make_exception_ptr 303 | */ 304 | template 305 | void* yieldExceptionTo(Coro *resumeCoro, const T& e) 306 | { 307 | void *received = currentCoro()->yieldExceptionTo(resumeCoro, ut::make_exception_ptr(e)); 308 | return received; 309 | } 310 | 311 | /** 312 | * Queue an action to run after resuming main coroutine. 313 | */ 314 | void postIdleAction(Action action); 315 | 316 | // 317 | // misc 318 | // 319 | 320 | /** Thrown on attempting yield to an invalid coroutine */ 321 | class YieldForbidden 322 | { 323 | public: 324 | static std::exception_ptr ptr(); 325 | }; 326 | 327 | /** Special exception for interrupting a coroutine */ 328 | class ForcedUnwind 329 | { 330 | public: 331 | static std::exception_ptr ptr(); 332 | }; 333 | 334 | /** 335 | * Helper function, yields ForcedUnwind exception to a coroutine. 336 | * 337 | * Safe for use while an exception is propagating. 338 | */ 339 | void forceUnwind(Coro *coro); 340 | 341 | } 342 | -------------------------------------------------------------------------------- /include/CppAwait/Log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "Config.h" 20 | #include 21 | 22 | namespace ut { 23 | 24 | enum LogLevel 25 | { 26 | LOGLEVEL_NONE, 27 | LOGLEVEL_WARN, 28 | LOGLEVEL_INFO, 29 | LOGLEVEL_DEBUG, 30 | LOGLEVEL_VERBOSE 31 | }; 32 | 33 | extern LogLevel gLogLevel; 34 | 35 | inline void setLogLevel(LogLevel logLevel) 36 | { 37 | gLogLevel = logLevel; 38 | } 39 | 40 | inline LogLevel logLevel() 41 | { 42 | return gLogLevel; 43 | } 44 | 45 | void implLog(LogLevel logLevel, const char *format, ...); 46 | 47 | } 48 | 49 | #ifdef UT_DISABLE_LOGGING 50 | 51 | #define ut_log_warn_( _format, ...) 52 | #define ut_log_info_( _format, ...) 53 | #define ut_log_debug_( _format, ...) 54 | #define ut_log_verbose_( _format, ...) 55 | 56 | #else // not UT_DISABLE_LOGGING 57 | 58 | // lazy argument evaluation 59 | // 60 | #define ut_impl_log_(_level, _format, ...) \ 61 | ut_multi_line_macro_begin_ \ 62 | if (_level <= ut::gLogLevel) { \ 63 | ut::implLog(_level, _format, ##__VA_ARGS__); \ 64 | } \ 65 | ut_multi_line_macro_end_ 66 | 67 | #define ut_log_warn_( _format, ...) ut_impl_log_(ut::LOGLEVEL_WARN, _format, ##__VA_ARGS__) 68 | #define ut_log_info_( _format, ...) ut_impl_log_(ut::LOGLEVEL_INFO, _format, ##__VA_ARGS__) 69 | #define ut_log_debug_( _format, ...) ut_impl_log_(ut::LOGLEVEL_DEBUG, _format, ##__VA_ARGS__) 70 | #define ut_log_verbose_( _format, ...) ut_impl_log_(ut::LOGLEVEL_VERBOSE, _format, ##__VA_ARGS__) 71 | 72 | #endif // UT_DISABLE_LOGGING 73 | -------------------------------------------------------------------------------- /include/CppAwait/YieldSequence.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file YieldSequence.h 19 | * 20 | * Declares a utility class for iterating over generators 21 | * 22 | */ 23 | 24 | #pragma once 25 | 26 | #include "Config.h" 27 | #include "Coro.h" 28 | #include "impl/Assert.h" 29 | #include 30 | #include 31 | 32 | namespace ut { 33 | 34 | /** 35 | * Adapts a generator coroutine for iteration 36 | */ 37 | template 38 | class YieldSequence 39 | { 40 | public: 41 | class Iterator; 42 | typedef Iterator iterator; 43 | 44 | /** 45 | * Wraps coroutine into an iterable sequence 46 | * 47 | * @param func Generator. May yield pointers to T or an exception. 48 | */ 49 | YieldSequence(Coro::Func func) 50 | : m(new Impl(std::move(func), Coro("YieldSequence"))) 51 | { 52 | Impl *impl = m.get(); 53 | 54 | m->coro.init([impl](void *startValue) { 55 | impl->func(startValue); 56 | }); 57 | } 58 | 59 | ~YieldSequence() 60 | { 61 | // TODO: consider invalidating iterators 62 | 63 | if (m && m->coro.isRunning()) { 64 | forceUnwind(&m->coro); 65 | } 66 | } 67 | 68 | /** Move constructor */ 69 | YieldSequence(YieldSequence&& other) 70 | : m(std::move(other.m)) 71 | { 72 | } 73 | 74 | /** Move assignment */ 75 | YieldSequence& operator=(YieldSequence&& other) 76 | { 77 | // TODO: consider invalidating iterators 78 | 79 | if (m && m->coro.isRunning()) { 80 | forceUnwind(&m->coro); 81 | } 82 | 83 | m = std::move(other.m); 84 | 85 | return *this; 86 | } 87 | 88 | /** 89 | * Returns a forward iterator 90 | * 91 | * May only be called once. Traversing sequence multiple times is not supported. 92 | */ 93 | iterator begin() 94 | { 95 | ut_assert_(m->currentValue == nullptr && "may not begin again"); 96 | ut_assert_(m->coro.isRunning() && "may not begin again"); 97 | 98 | Iterator it(m.get()); 99 | return ++it; 100 | } 101 | 102 | /** Returns sequence end */ 103 | iterator end() 104 | { 105 | return Iterator(nullptr); 106 | } 107 | 108 | /** 109 | * Forward iterator 110 | */ 111 | class Iterator 112 | { 113 | public: 114 | typedef std::forward_iterator_tag iterator_category; 115 | typedef T value_type; 116 | typedef ptrdiff_t difference_type; 117 | typedef T* pointer; 118 | typedef T& reference; 119 | 120 | Iterator() 121 | : mContainer(nullptr) { } 122 | 123 | T& operator*() 124 | { 125 | ut_assert_(mContainer != nullptr && "may not dereference end"); 126 | ut_assert_(mContainer->currentValue != nullptr); 127 | ut_assert_(mContainer->coro.isRunning()); 128 | 129 | return *((T *) mContainer->currentValue); 130 | } 131 | 132 | Iterator& operator++() 133 | { 134 | ut_assert_(mContainer != nullptr && "may not increment past end"); 135 | 136 | try { 137 | mContainer->currentValue = yieldTo(&mContainer->coro); 138 | 139 | if (mContainer->currentValue == nullptr) { 140 | // coroutine has finished 141 | ut_assert_(!mContainer->coro.isRunning() && "may not yield nullptr from coroutine"); 142 | mContainer = nullptr; 143 | } 144 | } catch (const ForcedUnwind&) { // coroutine interrupted, swallow exception 145 | mContainer->currentValue = nullptr; 146 | mContainer = nullptr; 147 | } catch (...) { // propagate other exceptions thrown by coroutine 148 | mContainer->currentValue = nullptr; 149 | mContainer = nullptr; 150 | throw; 151 | } 152 | 153 | return *this; 154 | } 155 | 156 | bool operator==(const Iterator& other) 157 | { 158 | return mContainer == other.mContainer; 159 | } 160 | 161 | bool operator!=(const Iterator& other) 162 | { 163 | return !(*this == other); 164 | } 165 | 166 | private: 167 | Iterator(typename YieldSequence::Impl *container) 168 | : mContainer(container) { } 169 | 170 | Iterator& operator++(int); // disable postfix increment 171 | 172 | typename YieldSequence::Impl *mContainer; 173 | 174 | friend class YieldSequence; 175 | }; 176 | 177 | private: 178 | YieldSequence(const YieldSequence& other); // noncopyable 179 | YieldSequence& operator=(const YieldSequence& other); // noncopyable 180 | 181 | struct Impl 182 | { 183 | Coro::Func func; 184 | Coro coro; 185 | void *currentValue; 186 | 187 | Impl(Coro::Func&& func, Coro&& coro) 188 | : func(std::move(func)) 189 | , coro(std::move(coro)) 190 | , currentValue(nullptr) 191 | { 192 | } 193 | }; 194 | 195 | std::unique_ptr m; 196 | }; 197 | 198 | } 199 | -------------------------------------------------------------------------------- /include/CppAwait/impl/Assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "../Config.h" 20 | 21 | #include 22 | #include 23 | 24 | #ifdef NDEBUG 25 | 26 | #define ut_assert_(_condition) 27 | #define ut_assert_msg_(_condition, _format, ...) 28 | 29 | #else 30 | 31 | #define ut_assert_(_condition) \ 32 | ut_multi_line_macro_begin_ \ 33 | if (!(_condition)) { \ 34 | fprintf(stderr, "CPP_ASYNC ASSERT FAILED: " #_condition "\n"); \ 35 | assert (false); \ 36 | } \ 37 | ut_multi_line_macro_end_ 38 | 39 | #define ut_assert_msg_(_condition, _format, ...) \ 40 | ut_multi_line_macro_begin_ \ 41 | if (!(_condition)) { \ 42 | fprintf(stderr, "CPP_ASYNC ASSERT FAILED: " #_condition " --- " _format "\n", ##__VA_ARGS__); \ 43 | assert (false); \ 44 | } \ 45 | ut_multi_line_macro_end_ 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /include/CppAwait/impl/Compatibility.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "../Config.h" 20 | #include "Assert.h" 21 | #include 22 | #include 23 | 24 | namespace ut { 25 | 26 | // MSVC10 workaround -- no std::make_exception_ptr() 27 | // 28 | template 29 | std::exception_ptr make_exception_ptr(const T& e) 30 | { 31 | ut_assert_(!std::uncaught_exception() && "disallowed while an exception is propagating"); 32 | 33 | std::exception_ptr eptr; 34 | 35 | try { 36 | throw e; 37 | } catch(...) { 38 | // MSVC10+ issue - std::current_exception returns empty here if 39 | // another exception is currently propagating 40 | eptr = std::current_exception(); 41 | } 42 | 43 | return eptr; 44 | } 45 | 46 | // MSVC10 workaround -- no conversion from std::exception_ptr to bool 47 | // 48 | inline bool is(const std::exception_ptr& eptr) 49 | { 50 | return !(eptr == std::exception_ptr()); 51 | } 52 | 53 | // MSVC10 workaround -- no std::declval() 54 | // 55 | template T&& declval(); 56 | 57 | // make_unique missing in C++ 11 58 | // 59 | 60 | template 61 | std::unique_ptr make_unique() 62 | { 63 | return std::unique_ptr(new T()); 64 | } 65 | 66 | template 67 | std::unique_ptr make_unique(Arg1&& arg1) 68 | { 69 | return std::unique_ptr(new T( 70 | std::forward(arg1))); 71 | } 72 | 73 | template 74 | std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2) 75 | { 76 | return std::unique_ptr(new T( 77 | std::forward(arg1), std::forward(arg2))); 78 | } 79 | 80 | template 81 | std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3) 82 | { 83 | return std::unique_ptr(new T( 84 | std::forward(arg1), std::forward(arg2), std::forward(arg3))); 85 | } 86 | 87 | template 88 | std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4) 89 | { 90 | return std::unique_ptr(new T( 91 | std::forward(arg1), std::forward(arg2), std::forward(arg3), 92 | std::forward(arg4))); 93 | } 94 | 95 | template 96 | std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5) 97 | { 98 | return std::unique_ptr(new T( 99 | std::forward(arg1), std::forward(arg2), std::forward(arg3), 100 | std::forward(arg4), std::forward(arg5))); 101 | } 102 | 103 | // 104 | // misc 105 | // 106 | 107 | template 108 | std::unique_ptr asUniquePtr(T&& value) 109 | { 110 | return ut::make_unique(std::move(value)); 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /include/CppAwait/impl/Foreach.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "../Config.h" 20 | 21 | namespace ut { 22 | 23 | // support for if/else trick 24 | 25 | template 26 | struct false_wrapper 27 | { 28 | false_wrapper(const T& value) : value(value) { } 29 | 30 | operator bool() const { return false; } 31 | 32 | T value; 33 | }; 34 | 35 | template 36 | false_wrapper make_false_wrapper(const T& value) 37 | { 38 | return false_wrapper(value); 39 | } 40 | 41 | template 42 | struct false_ref_wrapper 43 | { 44 | false_ref_wrapper(T& value) : value(value) { } 45 | 46 | operator bool() const { return false; } 47 | 48 | T& value; 49 | 50 | private: 51 | false_ref_wrapper& operator=(const false_ref_wrapper&); 52 | }; 53 | 54 | template 55 | false_ref_wrapper make_false_ref_wrapper(T& value) 56 | { 57 | return false_ref_wrapper(value); 58 | } 59 | 60 | template 61 | void increment(T& it) 62 | { 63 | ++it; 64 | } 65 | 66 | } 67 | 68 | // 69 | // This macro is a workaround for missing range-based for in Visual Studio 2010. 70 | // 71 | // It it similar to BOOST_FOREACH but faster: 72 | // - optimized builds: as fast as a hand written loop 73 | // - debug builds: ~30% slower than a hand written loop 74 | // 75 | // Use notes: 76 | // - rvalue VALS is not supported 77 | // - begin()/end() evaluated only once 78 | // - do not add/remove from the container while iterating! 79 | // 80 | // For the curious: http://www.artima.com/cppsource/foreach.html 81 | // 82 | 83 | #define ut_foreach_(VAL, VALS) \ 84 | if (auto _foreach_col = ::ut::make_false_ref_wrapper(VALS)) { } else \ 85 | if (auto _foreach_cur = ::ut::make_false_wrapper(std::begin(_foreach_col.value))) { } else \ 86 | if (auto _foreach_end = ::ut::make_false_wrapper(std::end(_foreach_col.value))) { } else \ 87 | for (bool _foreach_flag = true; \ 88 | _foreach_flag && _foreach_cur.value != _foreach_end.value; \ 89 | _foreach_flag ? ::ut::increment(_foreach_cur.value) : (void) 0) \ 90 | if ((_foreach_flag = false) == true) { } else \ 91 | for (VAL = *_foreach_cur.value; !_foreach_flag; _foreach_flag = true) 92 | -------------------------------------------------------------------------------- /include/CppAwait/impl/SharedFlag.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "../Config.h" 20 | #include 21 | 22 | namespace ut { 23 | 24 | // generic shared_ptr 25 | 26 | typedef std::shared_ptr SharedFlag; 27 | 28 | // 29 | // allocate flag -- possibly from a memory pool 30 | // 31 | 32 | SharedFlag allocateSharedFlag(void *value = nullptr); 33 | 34 | inline SharedFlag allocateSharedFlag(long long value) 35 | { 36 | return allocateSharedFlag((void *) value); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /include/CppAwait/impl/StringUtil.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "../Config.h" 20 | #include 21 | #include 22 | #include 23 | 24 | namespace ut { 25 | 26 | // 27 | // string utilities 28 | // 29 | 30 | #ifdef _MSC_VER 31 | 32 | # define snprintf c99_snprintf 33 | # define vsnprintf c99_vsnprintf 34 | 35 | int c99_snprintf(char *outBuf, size_t size, const char *format, ...); 36 | int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap); 37 | 38 | #endif // _MSC_VER 39 | 40 | 41 | int safe_printf(std::vector& outBuf, size_t pos, const char *format, ...); 42 | int safe_vprintf(std::vector& outBuf, size_t pos, const char *format, va_list ap); 43 | 44 | std::string string_printf(const char *format, ...); 45 | std::string string_vprintf(const char *format, va_list ap); 46 | 47 | } -------------------------------------------------------------------------------- /include/CppAwait/misc/Functional.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "../Config.h" 20 | #include "FastAction.h" 21 | #include 22 | 23 | namespace ut { 24 | using namespace std::placeholders; 25 | 26 | // Pick a FastAction large enough to wrap std::function without 27 | // dynamic allocation. This usually means efficient closures 28 | // for up to 4 pointers. 29 | // 30 | typedef ut::OptimalAction< 31 | sizeof(std::function) 32 | >::type Action; 33 | } 34 | -------------------------------------------------------------------------------- /include/CppAwait/misc/HybridVector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file HybridVector.h 19 | * 20 | * Declares the HybridVector class. 21 | * 22 | */ 23 | 24 | #pragma once 25 | 26 | #include "../Config.h" 27 | #include 28 | 29 | #ifndef Q_MOC_RUN // workaround for QTBUG-29331 30 | #include 31 | #endif 32 | 33 | namespace ut { 34 | 35 | /** Vector that switches to dynamic allocation after exceeding some predefined size */ 36 | template 37 | class HybridVector 38 | { 39 | public: 40 | typedef T value_type; 41 | typedef size_t size_type; 42 | typedef ptrdiff_t difference_type; 43 | typedef value_type& reference; 44 | typedef const value_type& const_reference; 45 | typedef value_type* pointer; 46 | typedef const value_type* const_pointer; 47 | typedef pointer iterator; 48 | typedef const_pointer const_iterator; 49 | 50 | HybridVector() 51 | : mVector(nullptr) 52 | { 53 | } 54 | 55 | ~HybridVector() 56 | { 57 | delete mVector; 58 | } 59 | 60 | HybridVector(const HybridVector& other) 61 | : mArray(other.mArray) 62 | { 63 | if (other.mVector) { 64 | mVector = new std::vector(*other.mVector); 65 | } else { 66 | mVector = nullptr; 67 | } 68 | } 69 | 70 | HybridVector& operator=(const HybridVector& other) 71 | { 72 | if (this != &other) { 73 | mArray = other.mArray; 74 | delete mVector; 75 | 76 | if (other.mVector) { 77 | mVector = new std::vector(*other.mVector); 78 | } else { 79 | mVector = nullptr; 80 | } 81 | } 82 | 83 | return *this; 84 | } 85 | 86 | HybridVector(HybridVector&& other) 87 | : mArray(std::move(other.mArray)) 88 | , mVector(other.mVector) 89 | { 90 | other.mVector = nullptr; 91 | } 92 | 93 | HybridVector& operator=(HybridVector&& other) 94 | { 95 | mArray = std::move(other.mArray); 96 | delete mVector; 97 | mVector = other.mVector; 98 | other.mVector = nullptr; 99 | 100 | return *this; 101 | } 102 | 103 | T& at(size_t pos) 104 | { 105 | return mVector ? mVector->at(pos) : mArray.at(pos); 106 | } 107 | 108 | const T& at(size_t pos) const 109 | { 110 | return mVector ? mVector->at(pos) : mArray.at(pos); 111 | } 112 | 113 | T& operator[](size_t pos) 114 | { 115 | return mVector ? (*mVector)[pos] : mArray[pos]; 116 | } 117 | 118 | const T& operator[](size_t pos) const 119 | { 120 | return mVector ? (*mVector)[pos] : mArray[pos]; 121 | } 122 | 123 | T& front() 124 | { 125 | return mVector ? mVector->front() : mArray.front(); 126 | } 127 | 128 | const T& front() const 129 | { 130 | return mVector ? mVector->front() : mArray.front(); 131 | } 132 | 133 | T& back() 134 | { 135 | return mVector ? mVector->back() : mArray.back(); 136 | } 137 | 138 | const T& back() const 139 | { 140 | return mVector ? mVector->back() : mArray.back(); 141 | } 142 | 143 | T* data() 144 | { 145 | return mVector ? mVector->data() : mArray.data(); 146 | } 147 | 148 | const T* data() const 149 | { 150 | return mVector ? mVector->data() : mArray.data(); 151 | } 152 | 153 | T* begin() 154 | { 155 | return (T*) cbegin(); 156 | }; 157 | 158 | const T* begin() const 159 | { 160 | return cbegin(); 161 | }; 162 | 163 | const T* cbegin() const 164 | { 165 | if (mVector) { 166 | return mVector->empty() ? nullptr : &mVector->front(); 167 | } else { 168 | return mArray.empty() ? nullptr : &mArray.front(); 169 | } 170 | }; 171 | 172 | T* end() 173 | { 174 | return (T*) cend(); 175 | }; 176 | 177 | const T* end() const 178 | { 179 | return cend(); 180 | }; 181 | 182 | const T* cend() const 183 | { 184 | if (mVector) { 185 | return mVector->empty() ? nullptr : &mVector->back() + 1; 186 | } else { 187 | return mArray.empty() ? nullptr : &mArray.back() + 1; 188 | } 189 | }; 190 | 191 | size_t empty() const 192 | { 193 | return mVector ? mVector->empty() : mArray.empty(); 194 | } 195 | 196 | size_t size() const 197 | { 198 | return mVector ? mVector->size() : mArray.size(); 199 | } 200 | 201 | void reserve(size_t newCapacity) 202 | { 203 | if (mVector) { 204 | mVector->reserve(newCapacity); 205 | } else { 206 | if (newCapacity > mArray.capacity()) { 207 | switchToHeap(); 208 | reserve(newCapacity); 209 | } 210 | } 211 | } 212 | 213 | size_t capacity() const 214 | { 215 | return mVector ? mVector->capacity() : mArray.capacity(); 216 | } 217 | 218 | void clear() 219 | { 220 | if (mVector) { 221 | mVector->clear(); 222 | } else { 223 | mArray.clear(); 224 | } 225 | } 226 | 227 | void insert(const_iterator pos, const T& value) 228 | { 229 | if (mVector) { 230 | mVector->insert(vPos(pos), value); 231 | } else { 232 | if (mArray.size() < mArray.capacity()) { 233 | mArray.insert(aPos(pos), value); 234 | } else { 235 | size_t index = pos - begin(); 236 | switchToHeap(); 237 | insert(begin() + index, value); 238 | } 239 | } 240 | } 241 | 242 | void insert(const_iterator pos, T&& value) 243 | { 244 | if (mVector) { 245 | mVector->insert(vPos(pos), std::move(value)); 246 | } else { 247 | if (mArray.size() < mArray.capacity()) { 248 | mArray.insert(aPos(pos), std::move(value)); 249 | } else { 250 | size_t index = pos - begin(); 251 | switchToHeap(); 252 | insert(begin() + index, std::move(value)); 253 | } 254 | } 255 | } 256 | 257 | void insert(const_iterator pos, size_t count, const T& value) 258 | { 259 | if (mVector) { 260 | mVector->insert(vPos(pos), count, value); 261 | } else { 262 | if (mArray.size() + count <= mArray.capacity()) { 263 | mArray.insert(aPos(pos), count, value); 264 | } else { 265 | size_t index = pos - begin(); 266 | switchToHeap(); 267 | insert(begin() + index, count, value); 268 | } 269 | } 270 | } 271 | 272 | template 273 | void insert(const_iterator pos, InputIt first, InputIt last) 274 | { 275 | if (mVector) { 276 | mVector->insert(vPos(pos), first, last); 277 | } else { 278 | size_t count = (size_t) (last - first); 279 | 280 | if (mArray.size() + count <= mArray.capacity()) { 281 | mArray.insert(aPos(pos), first, last); 282 | } else { 283 | size_t index = pos - begin(); 284 | switchToHeap(); 285 | insert(begin() + index, first, last); 286 | } 287 | } 288 | } 289 | 290 | void erase(const_iterator pos) 291 | { 292 | if (mVector) { 293 | mVector->erase(vPos(pos)); 294 | } else { 295 | mArray.erase(aPos(pos)); 296 | } 297 | } 298 | 299 | void erase(const_iterator first, const_iterator last) 300 | { 301 | if (mVector) { 302 | mVector->erase(vPos(first), vPos(last)); 303 | } else { 304 | mArray.erase(aPos(first), aPos(last)); 305 | } 306 | } 307 | 308 | void push_back(const T& value) 309 | { 310 | if (mVector) { 311 | mVector->push_back(value); 312 | } else { 313 | if (mArray.size() < mArray.capacity()) { 314 | mArray.push_back(value); 315 | } else { 316 | switchToHeap(); 317 | push_back(value); 318 | } 319 | } 320 | } 321 | 322 | void push_back(T&& value) 323 | { 324 | if (mVector) { 325 | mVector->push_back(std::move(value)); 326 | } else { 327 | if (mArray.size() < mArray.capacity()) { 328 | mArray.push_back(std::move(value)); 329 | } else { 330 | switchToHeap(); 331 | push_back(std::move(value)); 332 | } 333 | } 334 | } 335 | 336 | void pop_back() 337 | { 338 | if (mVector) { 339 | mVector->pop_back(); 340 | } else { 341 | mArray.pop_back(); 342 | } 343 | } 344 | 345 | void resize(size_t count) 346 | { 347 | if (mVector) { 348 | mVector->resize(count); 349 | } else { 350 | if (count <= mArray.capacity()) { 351 | mArray.resize(count); 352 | } else { 353 | switchToHeap(); 354 | resize(count); 355 | } 356 | } 357 | } 358 | 359 | void resize(size_t count, const T& value) 360 | { 361 | if (mVector) { 362 | mVector->resize(count, value); 363 | } else { 364 | if (count <= mArray.capacity()) { 365 | mArray.resize(count, value); 366 | } else { 367 | switchToHeap(); 368 | resize(count, value); 369 | } 370 | } 371 | } 372 | 373 | void swap(HybridVector& other) 374 | { 375 | std::swap(mVector, other.mVector); 376 | 377 | if (!mVector || !other.mVector) { 378 | mArray.swap(other.mArray); 379 | } 380 | } 381 | 382 | private: 383 | auto vPos(const_iterator pos) const 384 | -> typename std::vector::const_iterator 385 | { 386 | return mVector->begin() + (pos - begin()); 387 | } 388 | 389 | // std::vector insert() and erase() take const_iterators 390 | // since C++11, this overload is for legacy std libraries 391 | // 392 | auto vPos(const_iterator pos) 393 | -> typename std::vector::iterator 394 | { 395 | return mVector->begin() + (pos - begin()); 396 | } 397 | 398 | auto aPos(const_iterator pos) const 399 | -> typename boost::container::static_vector::const_iterator 400 | { 401 | return mArray.begin() + (pos - begin()); 402 | } 403 | 404 | void switchToHeap() 405 | { 406 | mVector = new std::vector(); 407 | mVector->reserve(2 * mArray.capacity()); 408 | 409 | for (auto it = mArray.begin(), end = mArray.end(); it != end; ++it) { 410 | mVector->push_back(std::move(*it)); 411 | } 412 | 413 | mArray.clear(); 414 | } 415 | 416 | boost::container::static_vector mArray; 417 | std::vector *mVector; 418 | }; 419 | 420 | template 421 | void swap(HybridVector& lhs, HybridVector& rhs) 422 | { 423 | lhs.swap(rhs); 424 | } 425 | 426 | } 427 | -------------------------------------------------------------------------------- /include/CppAwait/misc/OpaqueSharedPtr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file OpaqueSharedPtr.h 19 | * 20 | * Declares the OpaqueSharedPtr class. 21 | * 22 | */ 23 | 24 | #pragma once 25 | 26 | #include "../Config.h" 27 | #include 28 | #include 29 | 30 | namespace ut { 31 | 32 | 33 | /** 34 | * Handle to a shared_ptr with type erased 35 | * 36 | * OpaqueSharedPtr keeps some abstract resource alive until you no longer need it. 37 | * 38 | * shared_ptr should also work, but it doesn't handle const T. 39 | */ 40 | class OpaqueSharedPtr 41 | { 42 | public: 43 | /** Create an empty opaque reference */ 44 | OpaqueSharedPtr() { } 45 | 46 | /** Create an opaque reference from a regular shared_ptr */ 47 | template 48 | OpaqueSharedPtr(const std::shared_ptr& ref) 49 | : mHolder(new Holder(ref)) { } 50 | 51 | /** Create an opaque reference from a regular shared_ptr */ 52 | template 53 | OpaqueSharedPtr(std::shared_ptr&& ref) 54 | : mHolder(new Holder(std::move(ref))) { } 55 | 56 | /** Copy constructor */ 57 | OpaqueSharedPtr(const OpaqueSharedPtr& other) 58 | : mHolder(other.mHolder->clone()) { } 59 | 60 | /** Copy assignment */ 61 | OpaqueSharedPtr& operator=(const OpaqueSharedPtr& other) 62 | { 63 | if (this != &other) { 64 | mHolder.reset(other.mHolder->clone()); 65 | } 66 | 67 | return *this; 68 | } 69 | 70 | /** Move constructor */ 71 | OpaqueSharedPtr(OpaqueSharedPtr&& other) 72 | : mHolder(std::move(other.mHolder)) { } 73 | 74 | /** Move assignment */ 75 | OpaqueSharedPtr& operator=(OpaqueSharedPtr&& other) 76 | { 77 | mHolder = std::move(other.mHolder); 78 | 79 | return *this; 80 | } 81 | 82 | /** Clear reference */ 83 | void reset() 84 | { 85 | mHolder = nullptr; 86 | } 87 | 88 | /** Underlying shared_ptr use count */ 89 | long useCount() const 90 | { 91 | return mHolder->useCount(); 92 | } 93 | 94 | /* Check if an object is being referenced */ 95 | operator bool() 96 | { 97 | return mHolder.get() != nullptr; 98 | } 99 | 100 | private: 101 | class HolderBase 102 | { 103 | public: 104 | virtual ~HolderBase() { } 105 | 106 | virtual HolderBase* clone() const = 0; 107 | 108 | virtual long useCount() const = 0; 109 | }; 110 | 111 | template 112 | class Holder : public HolderBase 113 | { 114 | public: 115 | Holder(const std::shared_ptr& value) 116 | : mValue(value) { } 117 | 118 | Holder(std::shared_ptr&& value) 119 | : mValue(std::move(value)) { } 120 | 121 | Holder* clone() const 122 | { 123 | return new Holder(mValue); 124 | } 125 | 126 | long useCount() const 127 | { 128 | return mValue.use_count(); 129 | } 130 | 131 | private: 132 | std::shared_ptr mValue; 133 | }; 134 | 135 | std::unique_ptr mHolder; 136 | }; 137 | 138 | } 139 | -------------------------------------------------------------------------------- /include/CppAwait/misc/Scheduler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file Scheduler.h 19 | * 20 | * Declares some generic helpers for scheduling work on the main loop 21 | * of your program (Qt / GLib / MFC / Asio ...) 22 | * 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "../Config.h" 28 | #include "../misc/Functional.h" 29 | #include 30 | 31 | namespace ut { 32 | 33 | /** 34 | * Hook signature -- schedule an action 35 | * @param action action to run 36 | * 37 | * Note: 38 | * - action shall not be invoked from within this function 39 | * - schedule(a), schedule(b) implies a runs before b 40 | */ 41 | typedef void (*ScheduleFunc)(Action action); 42 | 43 | /** Setup scheduling hook */ 44 | void initScheduler(ScheduleFunc schedule); 45 | 46 | 47 | // 48 | // generic scheduling interface 49 | // 50 | 51 | /** Unique handle for a scheduled action, may be used to cancel the action */ 52 | class Ticket 53 | { 54 | public: 55 | /** Create a dummy ticket */ 56 | Ticket() { } 57 | 58 | /** Move constructor */ 59 | Ticket(Ticket&& other) 60 | : mAction(std::move(other.mAction)) { } 61 | 62 | /** Move assignment */ 63 | Ticket& operator=(Ticket&& other) 64 | { 65 | mAction = std::move(other.mAction); 66 | return *this; 67 | } 68 | 69 | /** Cancels action */ 70 | ~Ticket() { } 71 | 72 | /** 73 | * Check if ticket is attached to an action 74 | * 75 | * Returns true unless ticket is dummy or reset. This is unrelated 76 | * to action having run or not. 77 | */ 78 | operator bool() 79 | { 80 | return mAction.get() != nullptr; 81 | } 82 | 83 | /** Reset ticket, cancels action */ 84 | void reset() 85 | { 86 | mAction.reset(); 87 | } 88 | 89 | private: 90 | Ticket(std::shared_ptr&& action) 91 | : mAction(std::move(action)) { } 92 | 93 | Ticket(const Ticket& other); // noncopyable 94 | Ticket& operator=(const Ticket& other); // noncopyable 95 | 96 | std::shared_ptr mAction; 97 | 98 | friend Ticket scheduleWithTicket(Action action); 99 | }; 100 | 101 | /** Schedule an action */ 102 | void schedule(Action action); 103 | 104 | /** Schedule an action. Supports cancellation: destroying the ticket will implicitly cancel the action */ 105 | Ticket scheduleWithTicket(Action action); 106 | 107 | } 108 | -------------------------------------------------------------------------------- /include/CppAwait/misc/ScopeGuard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file ScopeGuard.h 19 | * 20 | * Declares the ScopeGuard class. 21 | * 22 | */ 23 | 24 | #pragma once 25 | 26 | #include "../Config.h" 27 | #include "../impl/Assert.h" 28 | #include "../Log.h" 29 | 30 | namespace ut { 31 | 32 | /** Classic scope guard for RAII */ 33 | template 34 | class ScopeGuard 35 | { 36 | public: 37 | typedef ScopeGuard type; 38 | 39 | /** 40 | * Create a dummy scope guard 41 | */ 42 | explicit ScopeGuard() 43 | : mIsDismissed(true) { } 44 | 45 | /** 46 | * Create a scope guard 47 | * @param cleanup functor to call at end of scope 48 | */ 49 | explicit ScopeGuard(const F& cleanup) 50 | : mIsDismissed(false) 51 | , mCleanup(cleanup) { } 52 | 53 | /** 54 | * Create a scope guard 55 | * @param cleanup functor to call at end of scope 56 | */ 57 | explicit ScopeGuard(F&& cleanup) 58 | : mIsDismissed(false) 59 | , mCleanup(std::move(cleanup)) { } 60 | 61 | /** 62 | * Move constructor 63 | */ 64 | ScopeGuard(ScopeGuard&& other) 65 | : mIsDismissed(other.mIsDismissed) 66 | , mCleanup(std::move(other.mCleanup)) 67 | { 68 | other.mIsDismissed = true; 69 | } 70 | 71 | /** 72 | * Move assignment 73 | */ 74 | ScopeGuard& operator=(ScopeGuard&& other) 75 | { 76 | mIsDismissed = other.mIsDismissed; 77 | other.mIsDismissed = true; 78 | mCleanup = std::move(other.mCleanup); 79 | 80 | return *this; 81 | } 82 | 83 | /** Perform cleanup unless dismissed */ 84 | ~ScopeGuard() 85 | { 86 | if (!mIsDismissed) { 87 | try { 88 | mCleanup(); 89 | } catch (const std::exception& ex) { 90 | (void) ex; 91 | ut_assert_msg_(false, "ScopeGuard caught a %s exception: %s", typeid(ex).name(), ex.what()); 92 | } catch (...) { 93 | ut_assert_msg_(false, "ScopeGuard caught exception"); 94 | } 95 | } 96 | } 97 | 98 | /* Dismiss guard */ 99 | void dismiss() const 100 | { 101 | mIsDismissed = true; 102 | } 103 | 104 | /* Check if dismissed */ 105 | bool isDismissed() const 106 | { 107 | return mIsDismissed; 108 | } 109 | 110 | void touch() const { /* avoids "variable unused" compiler warnings */ } 111 | 112 | private: 113 | ScopeGuard(const ScopeGuard&); // noncopyable 114 | ScopeGuard& operator=(const ScopeGuard&); // noncopyable 115 | 116 | mutable bool mIsDismissed; 117 | F mCleanup; 118 | }; 119 | 120 | /** Create a scope guard with template argument deduction */ 121 | template 122 | ScopeGuard makeScopeGuard(F cleanup) 123 | { 124 | return ScopeGuard(cleanup); 125 | } 126 | 127 | } 128 | 129 | // 130 | // These macros should be enough unless you intend to move ScopeGuard 131 | // 132 | 133 | /** Macro for creating anonymous scope guards */ 134 | #define ut_scope_guard_(cleanup) \ 135 | const auto& ut_anonymous_variable_(scopeGuard) = ut::makeScopeGuard(cleanup); \ 136 | ut_anonymous_variable_(scopeGuard).touch() 137 | 138 | /** Macro for creating named scope guards */ 139 | #define ut_named_scope_guard_(name, cleanup) \ 140 | const auto& name = ut::makeScopeGuard(cleanup) 141 | -------------------------------------------------------------------------------- /include/CppAwait/misc/Signals.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 Valentin Milea 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file Signals.h 19 | * 20 | * Declares the Signal class. 21 | * 22 | */ 23 | 24 | #pragma once 25 | 26 | #include "../Config.h" 27 | #include "../impl/SharedFlag.h" 28 | #include "../impl/Assert.h" 29 | #include "../impl/Foreach.h" 30 | #include "../misc/Functional.h" 31 | #include "../misc/HybridVector.h" 32 | #include 33 | 34 | namespace ut { 35 | 36 | template > 37 | class Signal; 38 | 39 | /** Allows disconnecting a single slot from signal */ 40 | class SignalConnection 41 | { 42 | public: 43 | /** Construct a dummy connection */ 44 | SignalConnection() { } 45 | 46 | /** Copy constructor */ 47 | SignalConnection(const SignalConnection& other) 48 | : mDisconnect(other.mDisconnect) { } 49 | 50 | /** Copy assignment */ 51 | SignalConnection& operator=(const SignalConnection& other) 52 | { 53 | if (this != &other) { 54 | mDisconnect = other.mDisconnect; 55 | } 56 | 57 | return *this; 58 | } 59 | 60 | /** Move constructor */ 61 | SignalConnection(SignalConnection&& other) 62 | : mDisconnect(std::move(other.mDisconnect)) { } 63 | 64 | /** Move assignment */ 65 | SignalConnection& operator=(SignalConnection&& other) 66 | { 67 | mDisconnect = std::move(other.mDisconnect); 68 | 69 | return *this; 70 | } 71 | 72 | /** 73 | * Disconnect associated slot 74 | * 75 | * You may disconnect slots while the signal is being emitted. 76 | */ 77 | void disconnect() 78 | { 79 | if (mDisconnect) { 80 | mDisconnect(); 81 | mDisconnect = nullptr; 82 | } 83 | } 84 | 85 | private: 86 | SignalConnection(ut::Action&& disconnect) 87 | : mDisconnect(std::move(disconnect)) { } 88 | 89 | ut::Action mDisconnect; 90 | 91 | template 92 | friend class Signal; 93 | }; 94 | 95 | /** Lightweight signal - single threaded, no MPL */ 96 | template 97 | class Signal 98 | { 99 | public: 100 | /** Slot signature */ 101 | typedef Slot slot_type; 102 | 103 | /** Signal alias */ 104 | typedef Signal signal_type; 105 | 106 | /** 107 | * Connect a slot 108 | * 109 | * You may connect slots while the signal is being emitted. 110 | * 111 | * @param slot slot to connect 112 | * @return a connection object that may be used to disconnect slot 113 | */ 114 | SignalConnection connect(slot_type slot) 115 | { 116 | auto disconnectFlag = allocateSharedFlag(this); 117 | 118 | if (mIsEmitting) { 119 | mHooksToAdd.push_back(Hook(std::move(slot), disconnectFlag)); 120 | } else { 121 | mHooks.push_back(Hook(std::move(slot), disconnectFlag)); 122 | } 123 | 124 | return SignalConnection([disconnectFlag]() { 125 | auto thiz = (signal_type *) *disconnectFlag; 126 | 127 | if (thiz) { 128 | *disconnectFlag = nullptr; 129 | 130 | thiz->mNumCanceled++; 131 | thiz->trimCanceled(); 132 | } 133 | }); 134 | } 135 | 136 | /** 137 | * Connect a slot 138 | * 139 | * This version is slightly faster, but the slot can't be disconnected. 140 | * 141 | * @param slot slot to connect 142 | */ 143 | void connectLite(slot_type slot) 144 | { 145 | if (mIsEmitting) { 146 | mHooksToAdd.push_back(Hook(std::move(slot))); 147 | } else { 148 | mHooks.push_back(Hook(std::move(slot))); 149 | } 150 | } 151 | 152 | /** 153 | * Disconnect all slots 154 | * 155 | * You may disconnect slots while the signal is being emitted. 156 | */ 157 | void disconnectAll() 158 | { 159 | ut_foreach_(auto& hook, mHooksToAdd) { 160 | hook.cancel(); 161 | } 162 | ut_foreach_(auto& hook, mHooks) { 163 | hook.cancel(); 164 | } 165 | 166 | trimCanceled(); 167 | } 168 | 169 | protected: 170 | /** Abstract class */ 171 | Signal() 172 | : mNumCanceled(0) 173 | , mIsEmitting(nullptr) 174 | { 175 | } 176 | 177 | ~Signal() 178 | { 179 | if (!mHooksToAdd.empty()) { 180 | ut_foreach_(auto& hook, mHooksToAdd) { 181 | hook.cancel(); 182 | } 183 | } 184 | ut_foreach_(auto& hook, mHooks) { 185 | hook.cancel(); 186 | } 187 | 188 | if (mIsEmitting) { 189 | // break out of emit loop 190 | *mIsEmitting = false; 191 | } 192 | } 193 | 194 | template 195 | void emitSignal(F&& caller) 196 | { 197 | ut_assert_(!mIsEmitting && "may not emit signal from a slot"); 198 | ut_assert_(mNumCanceled == 0); 199 | ut_assert_(mHooksToAdd.empty()); 200 | 201 | size_t n = mHooks.size(); 202 | if (n == 0) { 203 | return; 204 | } 205 | 206 | bool isEmitting = true; 207 | mIsEmitting = &isEmitting; 208 | 209 | for (size_t i = 0; i < n; i++) { 210 | const Hook& hook = mHooks[i]; 211 | 212 | if (mNumCanceled == 0 || !hook.isCanceled()) { 213 | try { 214 | caller(hook.slot()); 215 | 216 | if (!isEmitting) { 217 | return; 218 | } 219 | } catch (...) { 220 | if (isEmitting) { 221 | mIsEmitting = nullptr; 222 | trimCanceled(); 223 | } 224 | throw; 225 | } 226 | } 227 | } 228 | ut_assert_(n == mHooks.size()); 229 | 230 | if (!mHooksToAdd.empty()) { 231 | ut_foreach_(auto& hook, mHooksToAdd) { 232 | mHooks.push_back(std::move(hook)); 233 | } 234 | mHooksToAdd.clear(); 235 | } 236 | 237 | mIsEmitting = nullptr; 238 | trimCanceled(); 239 | } 240 | 241 | private: 242 | Signal(const Signal&); // noncopyable 243 | Signal& operator=(const Signal&); // noncopyable 244 | 245 | void trimCanceled() 246 | { 247 | if (mNumCanceled == 0 || mIsEmitting) { 248 | return; 249 | } 250 | 251 | if (mNumCanceled == mHooks.size()) { 252 | #ifndef NDEBUG 253 | ut_foreach_(auto& hook, mHooks) { 254 | ut_assert_(hook.isCanceled()); 255 | } 256 | #endif 257 | mHooks.clear(); 258 | } else { 259 | auto pos = std::remove_if(mHooks.begin(), mHooks.end(), 260 | [](const Hook& hook) { 261 | return hook.isCanceled(); 262 | }); 263 | 264 | ut_assert_((size_t) (mHooks.end() - pos) == mNumCanceled); 265 | 266 | mHooks.erase(pos, mHooks.end()); 267 | } 268 | 269 | mNumCanceled = 0; 270 | } 271 | 272 | struct Hook 273 | { 274 | Hook(slot_type&& slot, const std::shared_ptr& disconnectFlag) 275 | : mSlot(std::move(slot)) 276 | , mDisconnectFlag(disconnectFlag) 277 | , mIsCanceled(false) { } 278 | 279 | Hook(slot_type&& slot) 280 | : mSlot(std::move(slot)) 281 | , mIsCanceled(false) { } 282 | 283 | Hook(Hook&& other) 284 | : mSlot(std::move(other.mSlot)) 285 | , mDisconnectFlag(std::move(other.mDisconnectFlag)) 286 | , mIsCanceled(other.mIsCanceled) 287 | { 288 | other.mIsCanceled = true; 289 | } 290 | 291 | Hook& operator=(Hook&& other) 292 | { 293 | mSlot = std::move(other.mSlot); 294 | mDisconnectFlag = std::move(other.mDisconnectFlag); 295 | mIsCanceled = other.mIsCanceled; 296 | other.mIsCanceled = true; 297 | 298 | return *this; 299 | } 300 | 301 | const slot_type& slot() const 302 | { 303 | return mSlot; 304 | } 305 | 306 | bool isCanceled() const 307 | { 308 | if (mDisconnectFlag && *mDisconnectFlag == nullptr) { 309 | mDisconnectFlag.reset(); 310 | mIsCanceled = true; 311 | } 312 | 313 | return mIsCanceled; 314 | } 315 | 316 | void cancel() 317 | { 318 | if (mDisconnectFlag) { 319 | *mDisconnectFlag = nullptr; 320 | mDisconnectFlag.reset(); 321 | } 322 | 323 | mIsCanceled = true; 324 | } 325 | 326 | private: 327 | Hook(const Hook& other); // noncopyable 328 | Hook& operator=(const Hook& other); // noncopyable 329 | 330 | slot_type mSlot; 331 | mutable std::shared_ptr mDisconnectFlag; 332 | mutable bool mIsCanceled; 333 | }; 334 | 335 | std::vector mHooksToAdd; 336 | HybridVector mHooks; 337 | 338 | size_t mNumCanceled; 339 | bool *mIsEmitting; 340 | }; 341 | 342 | /** Signal with 0 arguments */ 343 | class Signal0 : public Signal 344 | { 345 | public: 346 | Signal0() { } 347 | 348 | void operator()() 349 | { 350 | typedef Signal0::slot_type slot_type; 351 | 352 | this->emitSignal([](const slot_type& slot) { 353 | slot(); 354 | }); 355 | } 356 | 357 | private: 358 | Signal0(const Signal0&); // noncopyable 359 | Signal0& operator=(const Signal0&); // noncopyable 360 | }; 361 | 362 | /** Signal with 1 argument */ 363 | template 364 | class Signal1 : public Signal 365 | { 366 | public: 367 | Signal1() { } 368 | 369 | void operator()(const Arg1& arg1) 370 | { 371 | typedef typename Signal1::slot_type slot_type; 372 | 373 | this->emitSignal([&](const slot_type& slot) { 374 | slot(arg1); 375 | }); 376 | } 377 | 378 | private: 379 | Signal1(const Signal1&); // noncopyable 380 | Signal1& operator=(const Signal1&); // noncopyable 381 | }; 382 | 383 | /** Signal with 2 arguments */ 384 | template 385 | class Signal2 : public Signal 386 | { 387 | public: 388 | Signal2() { } 389 | 390 | void operator()(const Arg1& arg1, const Arg2& arg2) 391 | { 392 | typedef typename Signal2::slot_type slot_type; 393 | 394 | this->emitSignal([&](const slot_type& slot) { 395 | slot(arg1, arg2); 396 | }); 397 | } 398 | 399 | private: 400 | Signal2(const Signal2&); // noncopyable 401 | Signal2& operator=(const Signal2&); // noncopyable 402 | }; 403 | 404 | /** Signal with 3 arguments */ 405 | template 406 | class Signal3 : public Signal 407 | { 408 | public: 409 | Signal3() { } 410 | 411 | void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) 412 | { 413 | typedef typename Signal3::slot_type slot_type; 414 | 415 | this->emitSignal([&](const slot_type& slot) { 416 | slot(arg1, arg2, arg3); 417 | }); 418 | } 419 | 420 | private: 421 | Signal3(const Signal3&); // noncopyable 422 | Signal3& operator=(const Signal3&); // noncopyable 423 | }; 424 | 425 | /** Signal with 4 arguments */ 426 | template 427 | class Signal4 : public Signal 428 | { 429 | public: 430 | Signal4() { } 431 | 432 | void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) 433 | { 434 | typedef typename Signal4::slot_type slot_type; 435 | 436 | this->emitSignal([&](const slot_type& slot) { 437 | slot(arg1, arg2, arg3, arg4); 438 | }); 439 | } 440 | 441 | private: 442 | Signal4(const Signal4&); // noncopyable 443 | Signal4& operator=(const Signal4&); // noncopyable 444 | }; 445 | 446 | /** Signal with 5 arguments */ 447 | template 448 | class Signal5 : public Signal 449 | { 450 | public: 451 | Signal5() { } 452 | 453 | void operator()(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) 454 | { 455 | typedef typename Signal5::slot_type slot_type; 456 | 457 | this->emitSignal([&](const slot_type& slot) { 458 | slot(arg1, arg2, arg3, arg4, arg5); 459 | }); 460 | } 461 | 462 | private: 463 | Signal5(const Signal5&); // noncopyable 464 | Signal5& operator=(const Signal5&); // noncopyable 465 | }; 466 | 467 | } 468 | --------------------------------------------------------------------------------