Skip to content

Commit

Permalink
pw_bluetooth: Specify the Deleter when using std::unique_ptr
Browse files Browse the repository at this point in the history
In several cases we use std::unique_ptr<T> to return an object with RAII
semantics, meaning that some action is needed by the backend to release
the resource when the object goes out of scope. However, using using
std::unique_ptr<T> with the default deleter forces the pw_bluetooth
backend to use new/delete to allocate the T interface implementation.

This patch introduces a RaiiPtr<Api, void (Api::*OnDestroy)()> template
class as an alias of std::unique_ptr with a custom deleter that calls
the Api::OnDestroy parametric method instead of calling "delete". This
template class is used instead of the plain std::unique_ptr when
returning objects from the pw_bluetooth API for the all the "Api"
classes with only pure virtual methods.

In all 5 cases where this pattern was used the abstract class has one
more private pure virtual method (the one used as Api::OnDestroy
parameter) which must be implemented by the backend.

This allows a backend to manage the memory for the returned objects with
something other than new/delete, for example it can return a RaiiPtr to
a std::optional instance or to one of the existing pre-allocated objects
and re-use them  when needed. This is particularly useful in cases where
there can only be a fixed small number of objects for the given type,
like in the Scan or AdvertisingParameters case where there is only one
object at a time.

Bug: 250930246
Change-Id: I724ff5135e0f73f5fb74c7fc92abdffa6a9e2d2d
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/113210
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Alex Deymo <deymo@google.com>
Reviewed-by: Ben Lawson <benlawson@google.com>
  • Loading branch information
deymo authored and CQ Bot Account committed Oct 21, 2022
1 parent 6c593d7 commit faa1bea
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 28 deletions.
1 change: 1 addition & 0 deletions pw_bluetooth/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pw_cc_library(
"public/pw_bluetooth/hci.h",
"public/pw_bluetooth/host.h",
"public/pw_bluetooth/internal/hex.h",
"public/pw_bluetooth/internal/raii_ptr.h",
"public/pw_bluetooth/low_energy/advertising_data.h",
"public/pw_bluetooth/low_energy/bond_data.h",
"public/pw_bluetooth/low_energy/central.h",
Expand Down
1 change: 1 addition & 0 deletions pw_bluetooth/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pw_source_set("pw_bluetooth") {
"public/pw_bluetooth/hci.h",
"public/pw_bluetooth/host.h",
"public/pw_bluetooth/internal/hex.h",
"public/pw_bluetooth/internal/raii_ptr.h",
"public/pw_bluetooth/low_energy/advertising_data.h",
"public/pw_bluetooth/low_energy/bond_data.h",
"public/pw_bluetooth/low_energy/central.h",
Expand Down
1 change: 1 addition & 0 deletions pw_bluetooth/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pw_add_module_library(pw_bluetooth
public/pw_bluetooth/gatt/server.h
public/pw_bluetooth/gatt/types.h
public/pw_bluetooth/internal/hex.h
public/pw_bluetooth/internal/raii_ptr.h
public/pw_bluetooth/low_energy/advertising_data.h
public/pw_bluetooth/low_energy/bond_data.h
public/pw_bluetooth/low_energy/central.h
Expand Down
15 changes: 13 additions & 2 deletions pw_bluetooth/public/pw_bluetooth/gatt/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "pw_bluetooth/gatt/constants.h"
#include "pw_bluetooth/gatt/error.h"
#include "pw_bluetooth/gatt/types.h"
#include "pw_bluetooth/internal/raii_ptr.h"
#include "pw_bluetooth/result.h"
#include "pw_bluetooth/types.h"
#include "pw_containers/vector.h"
Expand Down Expand Up @@ -236,6 +237,17 @@ class RemoteService {

// Stops notifications for the characteristic with the given `handle`.
void StopNotifications(Handle handle);

private:
// Disconnect from the remote service. This method is called by the
// ~RemoteService::Ptr() when it goes out of scope, the API client should
// never call this method.
void Disconnect();

public:
// Movable RemoteService smart pointer. The remote server will remain
// connected until the returned RemoteService::Ptr is destroyed.
using Ptr = internal::RaiiPtr<RemoteService, &RemoteService::Disconnect>;
};

// Represents a GATT client that interacts with services on a GATT server.
Expand Down Expand Up @@ -278,8 +290,7 @@ class Client {
//
// This may fail with the following errors:
// kInvalidParameters - `handle` does not correspond to a known service.
virtual Result<Error, std::unique_ptr<RemoteService>> ConnectToService(
Handle handle) = 0;
virtual Result<Error, RemoteService::Ptr> ConnectToService(Handle handle) = 0;
};

} // namespace pw::bluetooth::gatt
28 changes: 21 additions & 7 deletions pw_bluetooth/public/pw_bluetooth/gatt/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "pw_bluetooth/gatt/error.h"
#include "pw_bluetooth/gatt/types.h"
#include "pw_bluetooth/internal/raii_ptr.h"
#include "pw_bluetooth/result.h"
#include "pw_bluetooth/types.h"
#include "pw_containers/vector.h"
Expand Down Expand Up @@ -55,8 +56,8 @@ class LocalServiceDelegate {
// Called when there is a fatal error related to this service that forces the
// service to close. LocalServiceDelegate methods will no longer be called.
// This invalidates the associated LocalService. It is OK to destroy both
// LocalServiceDelegate and the associated LocalService from within this
// method.
// `LocalServiceDelegate` and the associated `LocalService::Ptr` from within
// this method.
virtual void OnError(Error error) = 0;

// This notifies the current configuration of a particular
Expand Down Expand Up @@ -124,6 +125,7 @@ class LocalServiceDelegate {
virtual void MtuUpdate(PeerId peer_id, uint16_t mtu) = 0;
};

// Interface provided by the backend to interact with a published service.
// LocalService is valid for the lifetime of a published GATT service. It is
// used to control the service and send notifications/indications.
class LocalService {
Expand Down Expand Up @@ -183,6 +185,17 @@ class LocalService {
// this callback is called.
virtual void IndicateValue(const ValueChangedParameters& parameters,
Function<void(Result<Error>)>&& confirmation) = 0;

private:
// Unpublish the local service. This method is called by the
// ~LocalService::Ptr() when it goes out of scope, the API client should never
// call this method.
virtual void UnpublishService() = 0;

public:
// Movable LocalService smart pointer. When the LocalService::Ptr object is
// destroyed the service will be unpublished.
using Ptr = internal::RaiiPtr<LocalService, &LocalService::UnpublishService>;
};

// Interface for a GATT server that serves many GATT services.
Expand All @@ -204,22 +217,23 @@ class Server {
kInvalidIncludes = 4,
};

// The Result passed by PublishService.
using PublishServiceResult = Result<PublishServiceError, LocalService::Ptr>;

virtual ~Server() = default;

// Publishes the service defined by `info` and implemented by `delegate` so
// that it is available to all remote peers.
//
// The caller must assign distinct handles to the characteristics and
// descriptors listed in `info`. These identifiers will be used in requests
// sent to `delegate`. On success, a `LocalService` is returned. When the
// `LocalService` is destroyed or an error occurs
// sent to `delegate`. On success, a `LocalService::Ptr` is returned. When the
// `LocalService::Ptr` is destroyed or an error occurs
// (LocalServiceDelegate.OnError), the service will be unpublished.
virtual void PublishService(
const LocalServiceInfo& info,
LocalServiceDelegate* delegate,
Function<
void(Result<PublishServiceError, std::unique_ptr<LocalService>>)>&&
result_callback) = 0;
Function<void(PublishServiceResult)>&& result_callback) = 0;
};

} // namespace pw::bluetooth::gatt
67 changes: 67 additions & 0 deletions pw_bluetooth/public/pw_bluetooth/internal/raii_ptr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once

#include <memory>
#include <type_traits>

namespace pw::bluetooth::internal {

// Helper deleter struct to call the OnDestroy template parameter method when
// RaiiPtr<Api, OnDestroy> is destroyed.
template <class Api, void (Api::*OnDestroy)()>
struct RaiiPtrDeleter {
void operator()(Api* api) { (api->*OnDestroy)(); }
};

// Smart pointer class to expose an API with RAII semantics that calls a
// specific method of the API implementation when it is destroyed, instead of
// calling delete on the API object like the default std::unique_ptr deleter
// behavior. This allows to be used like if it was a std::unique_ptr<API> but
// leaving the memory management implementation details up to the backend
// implementing the API.
//
// Example usage:
// class SomeApi {
// public:
// virtual ~SomeApi() = default;
//
// virtual void MethodOne() = 0;
// virtual void MethodTwo(int) = 0;
//
// private:
// // Method used to release the resource in the backend.
// virtual void DeleteResource() = 0;
//
// public:
// using Ptr = RaiiPtr<SomeApi, &SomeApi::DeleteResource>;
// };
//
// // The concrete backend implementation.
// class BackendImplementation final : public SomeApi {
// public:
// void MethodOne() override { ... }
// void MethodTwo(int) override { ... }
// void DeleteResource() override { ... }
// };
//
// // Example using a static global resource object. GetResource should check
// // whether the global resource is in use.
// BackendImplementation backend_impl;
// SomeApi::Ptr GetResource() { ...; return &backend_impl; }
//
template <class Api, void (Api::*OnDestroy)()>
using RaiiPtr = std::unique_ptr<Api, RaiiPtrDeleter<Api, OnDestroy>>;

} // namespace pw::bluetooth::internal
33 changes: 22 additions & 11 deletions pw_bluetooth/public/pw_bluetooth/low_energy/central.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <memory>
#include <optional>

#include "pw_bluetooth/internal/raii_ptr.h"
#include "pw_bluetooth/low_energy/advertising_data.h"
#include "pw_bluetooth/low_energy/connection.h"
#include "pw_bluetooth/result.h"
Expand All @@ -39,6 +40,16 @@ class Central {
// Set a callback that will be called if the scan is stopped due to an error
// in the BLE stack.
virtual void SetErrorCallback(Function<void(ScanError)>&& callback) = 0;

private:
// Stop the current scan. This method is called by the ~Scan::Ptr() when it
// goes out of scope, the API client should never call this method.
virtual void StopScan() = 0;

public:
// Movable Scan smart pointer. The controller will continue scanning until
// the returned Scan::Ptr is destroyed.
using Ptr = internal::RaiiPtr<Scan, &Scan::StopScan>;
};

// Filter parameters for use during a scan. A discovered peer only matches the
Expand Down Expand Up @@ -195,6 +206,9 @@ class Central {
kInternal,
};

// The Result type returned by Connect() via the passed callback.
using ConnectResult = Result<ConnectError, Connection::Ptr>;

virtual ~Central() = default;

// Connect to the peer with the given identifier.
Expand All @@ -213,16 +227,14 @@ class Central {
// error occurs.
//
// Possible errors are documented in `ConnectError`.
virtual void Connect(
PeerId peer_id,
ConnectionOptions options,
Function<void(Result<ConnectError, std::unique_ptr<Connection>>)>&&
callback) = 0;
virtual void Connect(PeerId peer_id,
ConnectionOptions options,
Function<void(ConnectResult)>&& callback) = 0;

// Scans for nearby LE peripherals and broadcasters. The lifetime of the scan
// session is tied to the returned `Scan` object. Once a scan is started,
// `scan_result_callback` will be called with scan results. Only 1 scan may be
// active at a time. It is OK to destroy the `Scan` object in
// active at a time. It is OK to destroy the `Scan::Ptr` object in
// `scan_result_callback` to stop scanning (no more results will be returned).
//
// Parameters:
Expand All @@ -234,11 +246,10 @@ class Central {
// call.
// `scan_started_callback` - Called with a `Scan` object if the
// scan successfully starts, or a `ScanError` otherwise.
virtual void Scan(
ScanOptions options,
Function<void(ScanResult)>&& scan_result_callback,
Function<void(Result<StartScanError, std::unique_ptr<Scan>>)>&&
scan_started_callback) = 0;
virtual void Scan(ScanOptions options,
Function<void(ScanResult)>&& scan_result_callback,
Function<void(Result<StartScanError, Scan::Ptr>)>&&
scan_started_callback) = 0;
};

} // namespace pw::bluetooth::low_energy
12 changes: 12 additions & 0 deletions pw_bluetooth/public/pw_bluetooth/low_energy/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#pragma once

#include "pw_bluetooth/gatt/client.h"
#include "pw_bluetooth/internal/raii_ptr.h"
#include "pw_bluetooth/types.h"

namespace pw::bluetooth::low_energy {
Expand Down Expand Up @@ -156,6 +157,17 @@ class Connection {
virtual void RequestConnectionParameterUpdate(
RequestedConnectionParameters parameters,
Function<void(Result<ConnectionParameterUpdateError>)>&& callback) = 0;

private:
// Request to disconnect this connection. This method is called by the
// ~Connection::Ptr() when it goes out of scope, the API client should never
// call this method.
virtual void Disconnect() = 0;

public:
// Movable Connection smart pointer. When Connection::Ptr is destroyed the
// Connection will disconnect automatically.
using Ptr = internal::RaiiPtr<Connection, &Connection::Disconnect>;
};

} // namespace pw::bluetooth::low_energy
28 changes: 20 additions & 8 deletions pw_bluetooth/public/pw_bluetooth/low_energy/peripheral.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <cstdint>
#include <memory>

#include "pw_bluetooth/internal/raii_ptr.h"
#include "pw_bluetooth/low_energy/advertising_data.h"
#include "pw_bluetooth/low_energy/connection.h"
#include "pw_bluetooth/result.h"
Expand All @@ -28,12 +29,11 @@ namespace pw::bluetooth::low_energy {
// `AdvertisedPeripheral` instances are valid for the duration of advertising.
class AdvertisedPeripheral {
public:
// Destroying an instance will stop advertising.
virtual ~AdvertisedPeripheral() = default;

// Set a callback that will be called when an error occurs and advertising
// has been stopped (invalidating this object). It is OK to destroy this
// object from within `callback`.
// has been stopped (invalidating this object). It is OK to destroy the
// `AdvertisedPeripheral::Ptr` object from within `callback`.
virtual void SetErrorCallback(Closure callback) = 0;

// For connectable advertisements, this callback will be called when an LE
Expand All @@ -50,9 +50,21 @@ class AdvertisedPeripheral {
//
// If advertising is not stopped, this callback may be called with multiple
// connections over the lifetime of an advertisement. It is OK to destroy
// this object from within `callback` in order to stop advertising.
// the `AdvertisedPeripheral::Ptr` object from within `callback` in order to
// stop advertising.
virtual void SetConnectionCallback(
Function<void(std::unique_ptr<Connection>)>&& callback) = 0;
Function<void(Connection::Ptr)>&& callback) = 0;

private:
// Stop advertising. This method is called by the ~AdvertisedPeripheral::Ptr()
// when it goes out of scope, the API client should never call this method.
virtual void StopAdvertising() = 0;

public:
// Movable AdvertisedPeripheral smart pointer. The peripheral will continue
// advertising until the returned AdvertisedPeripheral::Ptr is destroyed.
using Ptr = internal::RaiiPtr<AdvertisedPeripheral,
&AdvertisedPeripheral::StopAdvertising>;
};

// Represents the LE Peripheral role, which advertises and is connected to.
Expand Down Expand Up @@ -117,15 +129,15 @@ class Peripheral {
kFailed = 6,
};

using AdvertiseCallback = Function<void(
Result<AdvertiseError, std::unique_ptr<AdvertisedPeripheral>>)>;
using AdvertiseCallback =
Function<void(Result<AdvertiseError, AdvertisedPeripheral::Ptr>)>;

virtual ~Peripheral() = default;

// Start advertising continuously as a LE peripheral. If advertising cannot
// be initiated then `result_callback` will be called with an error. Once
// started, advertising can be stopped by destroying the returned
// `AdvertisedPeripheral`.
// `AdvertisedPeripheral::Ptr`.
//
// If the system supports multiple advertising, this may be called as many
// times as there are advertising slots. To reconfigure an advertisement,
Expand Down

0 comments on commit faa1bea

Please sign in to comment.