| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 1 | /* | 
 | 2 |  * Licensed to the Apache Software Foundation (ASF) under one | 
 | 3 |  * or more contributor license agreements. See the NOTICE file | 
 | 4 |  * distributed with this work for additional information | 
 | 5 |  * regarding copyright ownership. The ASF licenses this file | 
 | 6 |  * to you under the Apache License, Version 2.0 (the | 
 | 7 |  * "License"); you may not use this file except in compliance | 
 | 8 |  * with the License. You may obtain a copy of the License at | 
 | 9 |  * | 
 | 10 |  *   http://www.apache.org/licenses/LICENSE-2.0 | 
 | 11 |  * | 
 | 12 |  * Unless required by applicable law or agreed to in writing, | 
 | 13 |  * software distributed under the License is distributed on an | 
 | 14 |  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 | 15 |  * KIND, either express or implied. See the License for the | 
 | 16 |  * specific language governing permissions and limitations | 
 | 17 |  * under the License. | 
 | 18 |  */ | 
 | 19 |  | 
 | 20 | #ifndef _THRIFT_WINDOWS_OverlappedSubmissionThread_H_ | 
 | 21 | #define _THRIFT_WINDOWS_OverlappedSubmissionThread_H_ 1 | 
 | 22 |  | 
 | 23 | #ifndef _WIN32 | 
 | 24 | #error "OverlappedSubmissionThread.h is only usable on Windows" | 
 | 25 | #endif | 
 | 26 |  | 
 | 27 | #include <thrift/windows/Sync.h> | 
 | 28 | #include <boost/noncopyable.hpp> | 
 | 29 | #include <Windows.h> | 
 | 30 |  | 
 | 31 | /* | 
 | 32 |   *** Why does this class exist? | 
 | 33 |   In short, because we want to enable something similar to a "select" loop, on Windows, with | 
 | 34 |   named pipes.  The core of the "select" loop is a call to WaitForMultipleObjects.  So that means | 
 | 35 |   we need a signalable object that indicates when data is available. | 
 | 36 |  | 
 | 37 |   A pipe handle doesn't do that.  A pipe handle is signaled when a read or write completes, and if | 
 | 38 |   no one has called read or write, then the pipe handle is useless in WaitForMultipleObjects.  So | 
 | 39 |   instead, we use overlapped I/O.  With overlapped I/O, you call read, and associate an event with | 
 | 40 |   the read.  When the read finishes, the event is signaled.  This means that when you create a pipe, | 
 | 41 |   you start a read.  When the customer calls read on your transport object, you wait for the last | 
 | 42 |   read to finish, and then kick off another. | 
 | 43 |  | 
 | 44 |   There is one big caveat to this though.  The thread that initiated the read must stay alive.  If | 
 | 45 |   the thread that initiated the read exits, then the read completes in an error state.  To ensure | 
 | 46 |   that the initiating thread stays alive, we create a singleton thread whose sole responsibility is | 
 | 47 |   to manage this overlapped I/O requests.  This introduces some overhead, but it is overhead that | 
 | 48 |   is necessary for correct behavior. | 
 | 49 |  | 
 | 50 |   This thread currently supports connect, read, and cancel io.  So far, I haven't needed to put any | 
 | 51 |   writes on this thread, but if needed, it could be done.  The client write buffer would need to be | 
 | 52 |   copied to ensure that it doesn't get invalidated. | 
 | 53 |  | 
 | 54 |   *** How does one use this class? | 
 | 55 |   Create a TOverlappedWorkItem, and fill in the action and "h", then call reset().  Your work item | 
 | 56 |   is now ready to be submitted to the overlapped submission thread.  Create a TAutoOverlapThread, | 
 | 57 |   and call thread->addWorkItem with your work item.  After addWorkItem completes, you may inspect | 
 | 58 |   last_error and success.  At some point in the future, call workItem.overlappedResults to wait | 
 | 59 |   until the operation has completed. | 
 | 60 | */ | 
 | 61 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 62 | namespace apache { | 
 | 63 | namespace thrift { | 
 | 64 | namespace transport { | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 65 |  | 
 | 66 | DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) struct TOverlappedWorkItem : public SLIST_ENTRY { | 
 | 67 |   TOverlappedWorkItem(); | 
 | 68 |  | 
 | 69 |   enum action_t { | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 70 |     UNKNOWN = 3000, | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 71 |     CONNECT, | 
 | 72 |     READ, | 
 | 73 |     CANCELIO, | 
 | 74 |     STOP, | 
 | 75 |   }; | 
 | 76 |  | 
 | 77 |   TAutoResetEvent doneSubmittingEvent; | 
 | 78 |   action_t action; | 
 | 79 |   HANDLE h; | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 80 |   uint8_t* buffer; | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 81 |   uint32_t buffer_len; | 
 | 82 |   OVERLAPPED overlap; | 
 | 83 |  | 
 | 84 |   DWORD last_error; | 
 | 85 |   BOOL success; | 
 | 86 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 87 |   void reset(uint8_t* buf, uint32_t len, HANDLE event); | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 88 |   uint32_t overlappedResults(bool signal_failure = true); | 
 | 89 |   bool process(); | 
 | 90 | }; | 
 | 91 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 92 | class TOverlappedSubmissionThread : boost::noncopyable { | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 93 | public: | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 94 |   void addWorkItem(TOverlappedWorkItem* item); | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 95 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 96 |   // singleton stuff | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 97 | public: | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 98 |   static TOverlappedSubmissionThread* acquire_instance(); | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 99 |   static void release_instance(); | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 100 |  | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 101 | private: | 
 | 102 |   static TCriticalSection instanceGuard_; | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 103 |   static TOverlappedSubmissionThread* instance_; | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 104 |   static uint32_t instanceRefCount_; | 
 | 105 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 106 |   // thread details | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 107 | private: | 
 | 108 |   TOverlappedSubmissionThread(); | 
 | 109 |   ~TOverlappedSubmissionThread(); | 
 | 110 |   void run(); | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 111 |   static unsigned __stdcall thread_proc(void* addr); | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 112 |  | 
 | 113 | private: | 
 | 114 |   DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) SLIST_HEADER workList_; | 
 | 115 |   TOverlappedWorkItem stopItem_; | 
 | 116 |   TAutoResetEvent workAvailableEvent_; | 
 | 117 |   HANDLE thread_; | 
 | 118 | }; | 
 | 119 |  | 
 | 120 | class TAutoOverlapThread : boost::noncopyable { | 
 | 121 | private: | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 122 |   TOverlappedSubmissionThread* p; | 
 | 123 |  | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 124 | public: | 
 | 125 |   TAutoOverlapThread() : p(TOverlappedSubmissionThread::acquire_instance()) {} | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 126 |   ~TAutoOverlapThread() { TOverlappedSubmissionThread::release_instance(); } | 
 | 127 |   TOverlappedSubmissionThread* operator->() { return p; } | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 128 | }; | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 129 | } | 
 | 130 | } | 
 | 131 | } // apache::thrift::transport | 
| Ben Craig | b2501a7 | 2013-09-13 12:29:43 -0500 | [diff] [blame] | 132 |  | 
 | 133 | #endif |