blob: 56684bb9baf74ae24d9e6107ab41e096fb46fdd2 [file] [log] [blame]
Ben Craigb2501a72013-09-13 12:29:43 -05001/*
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 Grochowski16a23a62014-11-13 15:33:38 +010062namespace apache {
63namespace thrift {
64namespace transport {
Ben Craigb2501a72013-09-13 12:29:43 -050065
66DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) struct TOverlappedWorkItem : public SLIST_ENTRY {
67 TOverlappedWorkItem();
68
69 enum action_t {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010070 UNKNOWN = 3000,
Ben Craigb2501a72013-09-13 12:29:43 -050071 CONNECT,
72 READ,
73 CANCELIO,
74 STOP,
75 };
76
77 TAutoResetEvent doneSubmittingEvent;
78 action_t action;
79 HANDLE h;
Konrad Grochowski16a23a62014-11-13 15:33:38 +010080 uint8_t* buffer;
Ben Craigb2501a72013-09-13 12:29:43 -050081 uint32_t buffer_len;
82 OVERLAPPED overlap;
83
84 DWORD last_error;
85 BOOL success;
86
Konrad Grochowski16a23a62014-11-13 15:33:38 +010087 void reset(uint8_t* buf, uint32_t len, HANDLE event);
Ben Craigb2501a72013-09-13 12:29:43 -050088 uint32_t overlappedResults(bool signal_failure = true);
89 bool process();
90};
91
Konrad Grochowski16a23a62014-11-13 15:33:38 +010092class TOverlappedSubmissionThread : boost::noncopyable {
Ben Craigb2501a72013-09-13 12:29:43 -050093public:
Konrad Grochowski16a23a62014-11-13 15:33:38 +010094 void addWorkItem(TOverlappedWorkItem* item);
Ben Craigb2501a72013-09-13 12:29:43 -050095
Konrad Grochowski16a23a62014-11-13 15:33:38 +010096 // singleton stuff
Ben Craigb2501a72013-09-13 12:29:43 -050097public:
Konrad Grochowski16a23a62014-11-13 15:33:38 +010098 static TOverlappedSubmissionThread* acquire_instance();
Ben Craigb2501a72013-09-13 12:29:43 -050099 static void release_instance();
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100100
Ben Craigb2501a72013-09-13 12:29:43 -0500101private:
102 static TCriticalSection instanceGuard_;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100103 static TOverlappedSubmissionThread* instance_;
Ben Craigb2501a72013-09-13 12:29:43 -0500104 static uint32_t instanceRefCount_;
105
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100106 // thread details
Ben Craigb2501a72013-09-13 12:29:43 -0500107private:
108 TOverlappedSubmissionThread();
109 ~TOverlappedSubmissionThread();
110 void run();
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100111 static unsigned __stdcall thread_proc(void* addr);
Ben Craigb2501a72013-09-13 12:29:43 -0500112
113private:
114 DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) SLIST_HEADER workList_;
115 TOverlappedWorkItem stopItem_;
116 TAutoResetEvent workAvailableEvent_;
117 HANDLE thread_;
118};
119
120class TAutoOverlapThread : boost::noncopyable {
121private:
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100122 TOverlappedSubmissionThread* p;
123
Ben Craigb2501a72013-09-13 12:29:43 -0500124public:
125 TAutoOverlapThread() : p(TOverlappedSubmissionThread::acquire_instance()) {}
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100126 ~TAutoOverlapThread() { TOverlappedSubmissionThread::release_instance(); }
127 TOverlappedSubmissionThread* operator->() { return p; }
Ben Craigb2501a72013-09-13 12:29:43 -0500128};
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100129}
130}
131} // apache::thrift::transport
Ben Craigb2501a72013-09-13 12:29:43 -0500132
133#endif