master
cpp 117 lines 2.91 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #include "precomp.h"
3 #include "p9lx.h"
4 using namespace std::chrono_literals;
5
6 namespace p9fs {
7
8 constexpr auto ThreadPoolTimeout = 10s;
9
10 ThreadPool g_ThreadPool;
11
12 // Create a socket class with a socket fd.
13 Socket::Socket(int socket) : m_Io{g_Watcher}
14 {
15 Reset(socket);
16 }
17
18 // Asynchronously wait for new connections.
19 Task<std::unique_ptr<ISocket>> Socket::AcceptAsync(CancelToken& token)
20 {
21 int socket = co_await p9fs::AcceptAsync(m_Io, token);
22 co_return std::make_unique<Socket>(socket);
23 }
24
25 // Asynchronously receive data.
26 Task<size_t> Socket::RecvAsync(gsl::span<gsl::byte> buffer, CancelToken& token)
27 {
28 return p9fs::RecvAsync(m_Io, buffer, token);
29 }
30
31 // Asynchronously send data.
32 Task<size_t> Socket::SendAsync(gsl::span<const gsl::byte> buffer, CancelToken& token)
33 {
34 size_t totalSent{};
35 do
36 {
37 totalSent += co_await p9fs::SendAsync(m_Io, buffer.subspan(totalSent), token);
38 } while (totalSent < buffer.size());
39
40 co_return totalSent;
41 }
42
43 void Socket::Reset(int socket)
44 {
45 m_Io.Reset(socket);
46 m_Socket.reset(socket);
47 }
48
49 // Create a new work item for a specific callback.
50 WorkItem::WorkItem(std::function<void()> callback) : m_Callback{callback}
51 {
52 }
53
54 // Submit the work item to the thread pool.
55 void WorkItem::Submit()
56 {
57 g_ThreadPool.SubmitWork(m_Callback);
58 }
59
60 // Create a new work item for a specific callback.
61 std::unique_ptr<IWorkItem> CreateWorkItem(std::function<void()> callback)
62 {
63 return std::make_unique<WorkItem>(callback);
64 }
65
66 // Create a new thread pool.
67 ThreadPool::ThreadPool() : m_MaxThreads{std::thread::hardware_concurrency()}
68 {
69 }
70
71 // Submit work to the thread pool.
72 void ThreadPool::SubmitWork(std::function<void()> callback)
73 {
74 std::lock_guard<std::mutex> lock{m_Lock};
75 m_WorkQueue.push(callback);
76 // If there are no threads to run the work right now, and it's not at the
77 // max, start a new thread.
78 if (m_AvailableThreads == 0 && m_RunningThreads < m_MaxThreads)
79 {
80 ++m_RunningThreads;
81 std::thread(&ThreadPool::WorkerCallback, this).detach();
82 }
83 else
84 {
85 m_Condition.notify_one();
86 }
87 }
88
89 // Runs a worker thread that executes queued work items.
90 void ThreadPool::WorkerCallback()
91 {
92 std::unique_lock<std::mutex> lock{m_Lock, std::defer_lock};
93 for (;;)
94 {
95 lock.lock();
96 ++m_AvailableThreads;
97 // Wait for work.
98 if (m_WorkQueue.empty() && !m_Condition.wait_for(lock, ThreadPoolTimeout, [this]() { return !m_WorkQueue.empty(); }))
99 {
100 // The wait timed out, so shut down this thread.
101 --m_AvailableThreads;
102 --m_RunningThreads;
103 return;
104 }
105
106 // Take ownership of the work item.
107 --m_AvailableThreads;
108 auto work = m_WorkQueue.front();
109 m_WorkQueue.pop();
110 lock.unlock();
111
112 // Run the work outside the lock.
113 work();
114 }
115 }
116
117 } // namespace p9fs