summaryrefslogtreecommitdiffstats
path: root/toolkit/components/bitsdownload/src/bits_interface/task/service_task.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/bitsdownload/src/bits_interface/task/service_task.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/bitsdownload/src/bits_interface/task/service_task.rs')
-rw-r--r--toolkit/components/bitsdownload/src/bits_interface/task/service_task.rs332
1 files changed, 332 insertions, 0 deletions
diff --git a/toolkit/components/bitsdownload/src/bits_interface/task/service_task.rs b/toolkit/components/bitsdownload/src/bits_interface/task/service_task.rs
new file mode 100644
index 0000000000..c66f127f7a
--- /dev/null
+++ b/toolkit/components/bitsdownload/src/bits_interface/task/service_task.rs
@@ -0,0 +1,332 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+use super::{
+ action::{
+ Action,
+ Action::{MonitorDownload, StartDownload},
+ ServiceAction,
+ },
+ client::{with_maybe_new_bits_client, ClientInitData},
+ dispatch_callback::{maybe_dispatch_request_via_callback, CallbackExpected},
+ error::{BitsTaskError, ErrorStage::CommandThread},
+ from_threadbound::{expect_from_threadbound_option, get_from_threadbound_option, DataType},
+ string::nsCString_to_OsString,
+ BitsRequest, BitsService,
+};
+
+use bits_client::{BitsClient, BitsMonitorClient, BitsProxyUsage, Guid};
+use crossbeam_utils::atomic::AtomicCell;
+use log::{info, warn};
+use moz_task::Task;
+use nserror::nsresult;
+use nsstring::nsCString;
+use xpcom::{
+ interfaces::{nsIBitsNewRequestCallback, nsIRequestObserver, nsISupports},
+ RefPtr, ThreadBoundRefPtr,
+};
+
+// D is the Data Type that the RunFn function needs to make S.
+// S is the Success Type that the RunFn returns on success and that the
+// DoneFn needs to make the BitsRequest.
+type RunFn<D, S> = fn(&D, &mut BitsClient) -> Result<S, BitsTaskError>;
+type DoneFn<D, S> = fn(
+ &D,
+ S,
+ &ClientInitData,
+ &BitsService,
+ &nsIRequestObserver,
+ Option<&nsISupports>,
+) -> Result<RefPtr<BitsRequest>, BitsTaskError>;
+
+pub struct ServiceTask<D, S> {
+ client_init_data: ClientInitData,
+ action: ServiceAction,
+ task_data: D,
+ run_fn: RunFn<D, S>,
+ done_fn: DoneFn<D, S>,
+ bits_service: AtomicCell<Option<ThreadBoundRefPtr<BitsService>>>,
+ observer: AtomicCell<Option<ThreadBoundRefPtr<nsIRequestObserver>>>,
+ context: AtomicCell<Option<ThreadBoundRefPtr<nsISupports>>>,
+ callback: AtomicCell<Option<ThreadBoundRefPtr<nsIBitsNewRequestCallback>>>,
+ result: AtomicCell<Option<Result<S, BitsTaskError>>>,
+}
+
+impl<D, S> ServiceTask<D, S>
+where
+ D: Sync + Send,
+ S: Sync + Send,
+{
+ pub fn new(
+ client_init_data: ClientInitData,
+ action: ServiceAction,
+ task_data: D,
+ run_fn: RunFn<D, S>,
+ done_fn: DoneFn<D, S>,
+ bits_service: RefPtr<BitsService>,
+ observer: RefPtr<nsIRequestObserver>,
+ context: Option<RefPtr<nsISupports>>,
+ callback: RefPtr<nsIBitsNewRequestCallback>,
+ ) -> ServiceTask<D, S> {
+ ServiceTask {
+ client_init_data,
+ action,
+ task_data,
+ run_fn,
+ done_fn,
+ bits_service: AtomicCell::new(Some(ThreadBoundRefPtr::new(bits_service))),
+ observer: AtomicCell::new(Some(ThreadBoundRefPtr::new(observer))),
+ context: AtomicCell::new(context.map(ThreadBoundRefPtr::new)),
+ callback: AtomicCell::new(Some(ThreadBoundRefPtr::new(callback))),
+ result: AtomicCell::new(None),
+ }
+ }
+}
+
+impl<D, S> Task for ServiceTask<D, S> {
+ fn run(&self) {
+ let result =
+ with_maybe_new_bits_client(&self.client_init_data, self.action.into(), |client| {
+ (self.run_fn)(&self.task_data, client)
+ });
+ self.result.store(Some(result));
+ }
+
+ fn done(&self) -> Result<(), nsresult> {
+ // If TaskRunnable.run() calls Task.done() to return a result
+ // on the main thread before TaskRunnable.run() returns on the worker
+ // thread, then the Task will get dropped on the worker thread.
+ //
+ // But the callback is an nsXPCWrappedJS that isn't safe to release
+ // on the worker thread. So we move it out of the Task here to ensure
+ // it gets released on the main thread.
+ let maybe_tb_callback = self.callback.swap(None);
+ // It also isn't safe to drop the BitsService RefPtr off-thread,
+ // because BitsService refcounting is non-atomic
+ let maybe_tb_service = self.bits_service.swap(None);
+ // The observer and context are also an nsXPCWrappedJS that aren't safe
+ // to release on the worker thread.
+ let maybe_tb_observer = self.observer.swap(None);
+ let maybe_tb_context = self.context.swap(None);
+
+ let action: Action = self.action.into();
+ let maybe_callback =
+ expect_from_threadbound_option(&maybe_tb_callback, DataType::Callback, action);
+
+ // Immediately invoked function expression to allow for the ? operator
+ let result: Result<RefPtr<BitsRequest>, BitsTaskError> = (|| {
+ let bits_service =
+ expect_from_threadbound_option(&maybe_tb_service, DataType::BitsService, action)?;
+ let observer =
+ expect_from_threadbound_option(&maybe_tb_observer, DataType::Observer, action)?;
+ let maybe_context =
+ get_from_threadbound_option(&maybe_tb_context, DataType::Context, action);
+ let success = self
+ .result
+ .swap(None)
+ .ok_or_else(|| BitsTaskError::missing_result(action))??;
+
+ (self.done_fn)(
+ &self.task_data,
+ success,
+ &self.client_init_data,
+ bits_service,
+ observer,
+ maybe_context,
+ )
+ })();
+ info!("BITS Interface Task completed: {:?}", result);
+ // We incremented the request count when we dispatched an action to
+ // start the job. Now we will decrement since the action completed.
+ // See the declaration of InitBitsService::request_count for details.
+ let bits_service_result =
+ expect_from_threadbound_option(&maybe_tb_service, DataType::BitsService, action);
+ match bits_service_result {
+ Ok(bits_service) => {
+ bits_service.dec_request_count();
+ }
+ Err(error) => {
+ warn!(
+ concat!(
+ "Unable to decrement the request count when finishing ServiceTask. ",
+ "The command thread may not be shut down. Error: {:?}"
+ ),
+ error
+ );
+ }
+ }
+
+ maybe_dispatch_request_via_callback(result, maybe_callback, CallbackExpected)
+ }
+}
+
+struct StartDownloadData {
+ download_url: nsCString,
+ save_rel_path: nsCString,
+ proxy: BitsProxyUsage,
+ no_progress_timeout_secs: u32,
+ update_interval_ms: u32,
+}
+
+struct StartDownloadSuccess {
+ guid: Guid,
+ monitor_client: BitsMonitorClient,
+}
+
+pub struct StartDownloadTask(ServiceTask<StartDownloadData, StartDownloadSuccess>);
+
+impl Task for StartDownloadTask {
+ fn run(&self) {
+ self.0.run();
+ }
+
+ fn done(&self) -> Result<(), nsresult> {
+ self.0.done()
+ }
+}
+
+impl StartDownloadTask {
+ pub fn new(
+ client_init_data: ClientInitData,
+ download_url: nsCString,
+ save_rel_path: nsCString,
+ proxy: BitsProxyUsage,
+ no_progress_timeout_secs: u32,
+ update_interval_ms: u32,
+ bits_service: RefPtr<BitsService>,
+ observer: RefPtr<nsIRequestObserver>,
+ context: Option<RefPtr<nsISupports>>,
+ callback: RefPtr<nsIBitsNewRequestCallback>,
+ ) -> StartDownloadTask {
+ StartDownloadTask(ServiceTask::new(
+ client_init_data,
+ ServiceAction::StartDownload,
+ StartDownloadData {
+ download_url,
+ save_rel_path,
+ proxy,
+ no_progress_timeout_secs,
+ update_interval_ms,
+ },
+ StartDownloadTask::run_fn,
+ StartDownloadTask::done_fn,
+ bits_service,
+ observer,
+ context,
+ callback,
+ ))
+ }
+
+ fn run_fn(
+ data: &StartDownloadData,
+ client: &mut BitsClient,
+ ) -> Result<StartDownloadSuccess, BitsTaskError> {
+ let url = nsCString_to_OsString(&data.download_url, StartDownload, CommandThread)?;
+ let path = nsCString_to_OsString(&data.save_rel_path, StartDownload, CommandThread)?;
+ let (success, monitor_client) = client
+ .start_job(
+ url,
+ path,
+ data.proxy,
+ data.no_progress_timeout_secs,
+ data.update_interval_ms,
+ )
+ .map_err(|pipe_error| BitsTaskError::from_pipe(StartDownload, pipe_error))??;
+ Ok(StartDownloadSuccess {
+ guid: success.guid,
+ monitor_client,
+ })
+ }
+
+ fn done_fn(
+ _data: &StartDownloadData,
+ success: StartDownloadSuccess,
+ client_init_data: &ClientInitData,
+ bits_service: &BitsService,
+ observer: &nsIRequestObserver,
+ maybe_context: Option<&nsISupports>,
+ ) -> Result<RefPtr<BitsRequest>, BitsTaskError> {
+ BitsRequest::new(
+ success.guid.clone(),
+ RefPtr::new(bits_service),
+ client_init_data.monitor_timeout_ms,
+ RefPtr::new(&observer),
+ maybe_context.map(RefPtr::new),
+ success.monitor_client,
+ ServiceAction::StartDownload,
+ )
+ }
+}
+
+struct MonitorDownloadData {
+ guid: Guid,
+ update_interval_ms: u32,
+}
+
+pub struct MonitorDownloadTask(ServiceTask<MonitorDownloadData, BitsMonitorClient>);
+
+impl Task for MonitorDownloadTask {
+ fn run(&self) {
+ self.0.run();
+ }
+
+ fn done(&self) -> Result<(), nsresult> {
+ self.0.done()
+ }
+}
+
+impl MonitorDownloadTask {
+ pub fn new(
+ client_init_data: ClientInitData,
+ guid: Guid,
+ update_interval_ms: u32,
+ bits_service: RefPtr<BitsService>,
+ observer: RefPtr<nsIRequestObserver>,
+ context: Option<RefPtr<nsISupports>>,
+ callback: RefPtr<nsIBitsNewRequestCallback>,
+ ) -> MonitorDownloadTask {
+ MonitorDownloadTask(ServiceTask::new(
+ client_init_data,
+ ServiceAction::MonitorDownload,
+ MonitorDownloadData {
+ guid,
+ update_interval_ms,
+ },
+ MonitorDownloadTask::run_fn,
+ MonitorDownloadTask::done_fn,
+ bits_service,
+ observer,
+ context,
+ callback,
+ ))
+ }
+
+ fn run_fn(
+ data: &MonitorDownloadData,
+ client: &mut BitsClient,
+ ) -> Result<BitsMonitorClient, BitsTaskError> {
+ let result = client
+ .monitor_job(data.guid.clone(), data.update_interval_ms)
+ .map_err(|pipe_error| BitsTaskError::from_pipe(MonitorDownload, pipe_error));
+ Ok(result??)
+ }
+
+ fn done_fn(
+ data: &MonitorDownloadData,
+ monitor_client: BitsMonitorClient,
+ client_init_data: &ClientInitData,
+ bits_service: &BitsService,
+ observer: &nsIRequestObserver,
+ maybe_context: Option<&nsISupports>,
+ ) -> Result<RefPtr<BitsRequest>, BitsTaskError> {
+ BitsRequest::new(
+ data.guid.clone(),
+ RefPtr::new(bits_service),
+ client_init_data.monitor_timeout_ms,
+ RefPtr::new(&observer),
+ maybe_context.map(RefPtr::new),
+ monitor_client,
+ ServiceAction::MonitorDownload,
+ )
+ }
+}