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 |