Skip to content

Commit

Permalink
Update vendor
Browse files Browse the repository at this point in the history
  • Loading branch information
tekezo committed Oct 3, 2024
1 parent ce75a14 commit 9dba7b1
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if(CMAKE_VERSION VERSION_LESS "3.1.0")
message(FATAL_ERROR "CMake >= 3.1.0 required")
endif()
cmake_policy(PUSH)
cmake_policy(VERSION 3.1.0...3.27)
cmake_policy(VERSION 3.1.0...3.28)
#----------------------------------------------------------------
# Generated CMake target import file.
#----------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#pragma once

// pqrs::osx::iokit_service_monitor v5.0
// pqrs::osx::iokit_service_monitor v6.0

// (C) Copyright Takayama Fumihiko 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See http://www.boost.org/LICENSE_1_0.txt)
// (See https://www.boost.org/LICENSE_1_0.txt)

// `pqrs::osx::iokit_service_monitor` can be used safely in a multi-threaded environment.

Expand All @@ -17,6 +17,7 @@
#include <pqrs/osx/iokit_registry_entry.hpp>
#include <pqrs/osx/iokit_types.hpp>
#include <pqrs/osx/kern_return.hpp>
#include <unordered_set>

namespace pqrs {
namespace osx {
Expand All @@ -41,13 +42,16 @@ class iokit_service_monitor final : dispatcher::extra::dispatcher_client {
: dispatcher_client(weak_dispatcher),
run_loop_thread_(run_loop_thread),
matching_dictionary_(matching_dictionary),
notification_port_(nullptr) {
notification_port_(nullptr),
scan_timer_(*this) {
}

virtual ~iokit_service_monitor(void) {
// dispatcher_client

detach_from_dispatcher();
detach_from_dispatcher([this] {
scan_timer_.stop();
});

// run_loop_thread

Expand Down Expand Up @@ -76,26 +80,6 @@ class iokit_service_monitor final : dispatcher::extra::dispatcher_client {
});
}

void async_invoke_service_matched(void) {
run_loop_thread_->enqueue(^{
if (*matching_dictionary_) {
io_iterator_t it = IO_OBJECT_NULL;
CFRetain(*matching_dictionary_);
kern_return r = IOServiceGetMatchingServices(type_safe::get(iokit_mach_port::null),
*matching_dictionary_,
&it);
if (!r) {
enqueue_to_dispatcher([this, r] {
error_occurred("IOServiceGetMatchingServices is failed.", r);
});
} else {
matched_callback(make_services(iokit_iterator(it)));
IOObjectRelease(it);
}
}
});
}

private:
// This method is executed in run_loop_thread_.
void start(void) {
Expand Down Expand Up @@ -165,6 +149,69 @@ class iokit_service_monitor final : dispatcher::extra::dispatcher_client {
}
}
}

//
// Setup scan timer
//

enqueue_to_dispatcher([this] {
registry_entry_ids_.clear();

// There are rare cases where IOFirstMatchNotification and kIOTerminatedNotification are not triggered.
// In such cases, service_terminated is never called for the terminated service.
// To avoid this issue, periodic scans will be performed, and callbacks will be invoked for services that didn't receive the notification.

scan_timer_.start(
[this] {
if (*matching_dictionary_) {
io_iterator_t it = IO_OBJECT_NULL;
CFRetain(*matching_dictionary_);
kern_return r = IOServiceGetMatchingServices(type_safe::get(iokit_mach_port::null),
*matching_dictionary_,
&it);
if (!r) {
enqueue_to_dispatcher([this, r] {
error_occurred("IOServiceGetMatchingServices is failed.", r);
});
} else {
auto services = make_services(iokit_iterator(it));
IOObjectRelease(it);

// Call service_matched

for (const auto& s : services) {
if (auto registry_entry_id = s.find_registry_entry_id()) {
invoke_service_matched(*registry_entry_id, s);
}
}

// Call service_terminated

std::unordered_set<iokit_registry_entry_id::value_t> terminated_registry_entry_ids;

for (const auto stored_registry_entry_id : registry_entry_ids_) {
bool found = false;
for (const auto& s : services) {
if (auto registry_entry_id = s.find_registry_entry_id()) {
if (stored_registry_entry_id == *registry_entry_id) {
found = true;
}
}
}

if (!found) {
terminated_registry_entry_ids.insert(stored_registry_entry_id);
}
}

for (const auto terminated_registry_entry_id : terminated_registry_entry_ids) {
invoke_service_terminated(terminated_registry_entry_id);
}
}
}
},
std::chrono::milliseconds(3000));
});
}

// This method is executed in run_loop_thread_.
Expand All @@ -180,6 +227,11 @@ class iokit_service_monitor final : dispatcher::extra::dispatcher_client {
IONotificationPortDestroy(notification_port_);
notification_port_ = nullptr;
}

enqueue_to_dispatcher([this] {
scan_timer_.stop();
registry_entry_ids_.clear();
});
}

static void static_matched_callback(void* _Nonnull refcon, io_iterator_t iterator) {
Expand All @@ -195,16 +247,24 @@ class iokit_service_monitor final : dispatcher::extra::dispatcher_client {
});
}

void matched_callback(const std::vector<iokit_registry_entry>& services) const {
void matched_callback(const std::vector<iokit_registry_entry>& services) {
for (const auto& s : services) {
if (auto registry_entry_id = s.find_registry_entry_id()) {
enqueue_to_dispatcher([this, registry_entry_id, s] {
service_matched(*registry_entry_id, s.get());
invoke_service_matched(*registry_entry_id, s);
});
}
}
}

// This method is executed in the dispatcher thread.
void invoke_service_matched(iokit_registry_entry_id::value_t registry_entry_id, iokit_registry_entry service) {
if (!registry_entry_ids_.contains(registry_entry_id)) {
registry_entry_ids_.insert(registry_entry_id);
service_matched(registry_entry_id, service.get());
}
}

static void static_terminated_callback(void* _Nonnull refcon, io_iterator_t iterator) {
auto self = static_cast<iokit_service_monitor*>(refcon);
if (!self) {
Expand All @@ -218,16 +278,24 @@ class iokit_service_monitor final : dispatcher::extra::dispatcher_client {
});
}

void terminated_callback(const std::vector<iokit_registry_entry>& services) const {
void terminated_callback(const std::vector<iokit_registry_entry>& services) {
for (const auto& s : services) {
if (auto registry_entry_id = s.find_registry_entry_id()) {
enqueue_to_dispatcher([this, registry_entry_id] {
service_terminated(*registry_entry_id);
invoke_service_terminated(*registry_entry_id);
});
}
}
}

// This method is executed in the dispatcher thread.
void invoke_service_terminated(iokit_registry_entry_id::value_t registry_entry_id) {
if (registry_entry_ids_.contains(registry_entry_id)) {
registry_entry_ids_.erase(registry_entry_id);
service_terminated(registry_entry_id);
}
}

static std::vector<iokit_registry_entry> make_services(const iokit_iterator& iterator) {
std::vector<iokit_registry_entry> services;

Expand All @@ -249,6 +317,9 @@ class iokit_service_monitor final : dispatcher::extra::dispatcher_client {
IONotificationPortRef _Nullable notification_port_;
iokit_iterator matched_notification_;
iokit_iterator terminated_notification_;

pqrs::dispatcher::extra::timer scan_timer_;
std::unordered_set<iokit_registry_entry_id::value_t> registry_entry_ids_;
};
} // namespace osx
} // namespace pqrs
Loading

0 comments on commit 9dba7b1

Please sign in to comment.