1// Copyright (c) 2023 Huawei Device Co., Ltd.
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use std::os::windows::io::{AsRawHandle, IntoRawHandle, RawHandle};
15use std::time::Duration;
16use std::{cmp, io};
17
18use super::Handle;
19use crate::sys::winapi::{
20    CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus, HANDLE,
21    INVALID_HANDLE_VALUE, OVERLAPPED, OVERLAPPED_ENTRY,
22};
23use crate::sys::Overlapped;
24use crate::{Event, Token};
25
26/// IOCP's HANDLE.
27#[derive(Debug)]
28pub(crate) struct CompletionPort {
29    handle: Handle,
30}
31
32impl CompletionPort {
33    /// Creates a new CompletionPort
34    pub(crate) fn new() -> io::Result<CompletionPort> {
35        let handle = unsafe { CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0) };
36        if handle == 0 {
37            Err(io::Error::last_os_error())
38        } else {
39            Ok(CompletionPort {
40                handle: Handle::new(handle),
41            })
42        }
43    }
44    /// Add t to CompletionPort
45    pub(crate) fn add_handle<T: AsRawHandle + ?Sized>(
46        &self,
47        token: usize,
48        t: &T,
49    ) -> io::Result<()> {
50        syscall!(
51            CreateIoCompletionPort(t.as_raw_handle() as HANDLE, self.handle.raw(), token, 0),
52            ()
53        )
54    }
55
56    /// Gets the completed events in the IOCP, and can get multiple events at
57    /// the same time. Return the set of completed events
58    pub(crate) fn get_results<'a>(
59        &self,
60        list: &'a mut [CompletionStatus],
61        timeout: Option<Duration>,
62    ) -> io::Result<&'a mut [CompletionStatus]> {
63        let len = cmp::min(list.len(), u32::MAX as usize) as u32;
64        let mut removed = 0;
65        let timeout = match timeout {
66            Some(dur) => {
67                let dur_ms = (dur + Duration::from_nanos(999_999)).as_millis();
68                cmp::min(dur_ms, u32::MAX as u128) as u32
69            }
70            None => u32::MAX,
71        };
72
73        syscall!(
74            GetQueuedCompletionStatusEx(
75                self.handle.raw(),
76                list.as_ptr() as *mut _,
77                len,
78                &mut removed,
79                timeout,
80                0,
81            ),
82            &mut list[..removed as usize]
83        )
84    }
85
86    /// Posts an I/O completion packet to an I/O completion port.
87    pub(crate) fn post(&self, token: Token) -> io::Result<()> {
88        let mut event = Event::new(token);
89        event.set_readable();
90
91        let status = event.as_completion_status();
92        syscall!(
93            PostQueuedCompletionStatus(
94                self.handle.raw(),
95                status.0.dwNumberOfBytesTransferred,
96                status.0.lpCompletionKey,
97                status.0.lpOverlapped
98            ),
99            ()
100        )
101    }
102}
103
104impl IntoRawHandle for CompletionPort {
105    fn into_raw_handle(self) -> RawHandle {
106        self.handle.into_raw()
107    }
108}
109
110/// Includes OVERLAPPED_ENTRY struct which contains operation result of IOCP
111#[derive(Clone, Copy)]
112pub struct CompletionStatus(OVERLAPPED_ENTRY);
113
114unsafe impl Send for CompletionStatus {}
115unsafe impl Sync for CompletionStatus {}
116
117impl CompletionStatus {
118    /// Creates a new `CompletionStatus`.
119    pub(crate) fn new(bytes: u32, token: usize, overlapped: *mut Overlapped) -> Self {
120        CompletionStatus(OVERLAPPED_ENTRY {
121            dwNumberOfBytesTransferred: bytes,
122            lpCompletionKey: token,
123            lpOverlapped: overlapped.cast::<_>(),
124            Internal: 0,
125        })
126    }
127
128    /// Creates a CompletionStatus with 0.
129    pub fn zero() -> Self {
130        Self::new(0, 0, std::ptr::null_mut())
131    }
132
133    /// Returns dwNumberOfBytesTransferred of OVERLAPPED_ENTRY.
134    pub fn bytes_transferred(&self) -> u32 {
135        self.0.dwNumberOfBytesTransferred
136    }
137
138    /// Returns lpCompletionKey of OVERLAPPED_ENTRY.
139    pub fn token(&self) -> usize {
140        self.0.lpCompletionKey
141    }
142
143    /// Returns lpOverlapped of OVERLAPPED_ENTRY.
144    pub fn overlapped(&self) -> *mut OVERLAPPED {
145        self.0.lpOverlapped
146    }
147
148    /// Returns OVERLAPPED_ENTRY struct.
149    pub fn entry(&self) -> &OVERLAPPED_ENTRY {
150        &self.0
151    }
152}
153