diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 383ab6cc3..a1385f4b8 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -243,7 +243,7 @@ typedef uintptr_t (*dc_callback_t) (dc_context_t* context, int event, uintptr_t * The object must be passed to the other context functions * and must be freed using dc_context_unref() after usage. */ -dc_context_t* dc_context_new (dc_callback_t cb, void* userdata, const char* os_name); +dc_context_t* dc_context_new (void* userdata, const char* os_name); /** @@ -540,12 +540,12 @@ int dc_is_configured (const dc_context_t* context); /** * TODO: Document */ -void dc_run (dc_context_t* context); +void dc_context_run (dc_context_t* context, dc_callback_t cb); /** * TODO: Document */ -void dc_shutdown (dc_context_t* context); +void dc_context_shutdown (dc_context_t* context); /** * This function can be called whenever there is a hint diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index ffea7344c..db37ffb42 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -57,7 +57,6 @@ use self::string::*; /// and protected by an [RwLock]. Other than that it needs to store /// the data which is passed into [dc_context_new]. pub struct ContextWrapper { - cb: Option, userdata: *mut libc::c_void, os_name: String, inner: RwLock>, @@ -91,7 +90,10 @@ impl ContextWrapper { /// /// This function makes it easy to log an error. unsafe fn error(&self, msg: &str) { - self.translate_cb(Event::Error(msg.to_string())); + self.with_inner(|ctx| { + ctx.call_cb(Event::Error(msg.to_string())); + }) + .unwrap(); } /// Unlock the context and execute a closure with it. @@ -122,88 +124,10 @@ impl ContextWrapper { } } } - - /// Translates the callback from the rust style to the C-style version. - unsafe fn translate_cb(&self, event: Event) { - if let Some(ffi_cb) = self.cb { - let event_id = event.as_id(); - match event { - Event::Info(msg) - | Event::SmtpConnected(msg) - | Event::ImapConnected(msg) - | Event::SmtpMessageSent(msg) - | Event::ImapMessageDeleted(msg) - | Event::ImapMessageMoved(msg) - | Event::ImapFolderEmptied(msg) - | Event::NewBlobFile(msg) - | Event::DeletedBlobFile(msg) - | Event::Warning(msg) - | Event::Error(msg) - | Event::ErrorNetwork(msg) - | Event::ErrorSelfNotInGroup(msg) => { - let data2 = CString::new(msg).unwrap_or_default(); - ffi_cb(self, event_id, 0, data2.as_ptr() as uintptr_t); - } - Event::MsgsChanged { chat_id, msg_id } - | Event::IncomingMsg { chat_id, msg_id } - | Event::MsgDelivered { chat_id, msg_id } - | Event::MsgFailed { chat_id, msg_id } - | Event::MsgRead { chat_id, msg_id } => { - ffi_cb( - self, - event_id, - chat_id.to_u32() as uintptr_t, - msg_id.to_u32() as uintptr_t, - ); - } - Event::ChatModified(chat_id) => { - ffi_cb(self, event_id, chat_id.to_u32() as uintptr_t, 0); - } - Event::ContactsChanged(id) | Event::LocationChanged(id) => { - let id = id.unwrap_or_default(); - ffi_cb(self, event_id, id as uintptr_t, 0); - } - Event::ConfigureProgress(progress) | Event::ImexProgress(progress) => { - ffi_cb(self, event_id, progress as uintptr_t, 0); - } - Event::ImexFileWritten(file) => { - let data1 = file.to_c_string().unwrap_or_default(); - ffi_cb(self, event_id, data1.as_ptr() as uintptr_t, 0); - } - Event::SecurejoinInviterProgress { - contact_id, - progress, - } - | Event::SecurejoinJoinerProgress { - contact_id, - progress, - } => { - ffi_cb( - self, - event_id, - contact_id as uintptr_t, - progress as uintptr_t, - ); - } - Event::SecurejoinMemberAdded { - chat_id, - contact_id, - } => { - ffi_cb( - self, - event_id, - chat_id.to_u32() as uintptr_t, - contact_id as uintptr_t, - ); - } - } - } - } } #[no_mangle] pub unsafe extern "C" fn dc_context_new( - cb: Option, userdata: *mut libc::c_void, os_name: *const libc::c_char, ) -> *mut dc_context_t { @@ -215,7 +139,6 @@ pub unsafe extern "C" fn dc_context_new( to_string_lossy(os_name) }; let ffi_ctx = ContextWrapper { - cb, userdata, os_name, inner: RwLock::new(None), @@ -257,17 +180,11 @@ pub unsafe extern "C" fn dc_open( return 0; } let ffi_context = &*context; - let rust_cb = move |_ctx: &Context, evt: Event| ffi_context.translate_cb(evt); let ctx = if blobdir.is_null() || *blobdir == 0 { - Context::new( - Box::new(rust_cb), - ffi_context.os_name.clone(), - as_path(dbfile).to_path_buf(), - ) + Context::new(ffi_context.os_name.clone(), as_path(dbfile).to_path_buf()) } else { Context::with_blobdir( - Box::new(rust_cb), ffi_context.os_name.clone(), as_path(dbfile).to_path_buf(), as_path(blobdir).to_path_buf(), @@ -471,17 +388,100 @@ pub unsafe extern "C" fn dc_is_configured(context: *mut dc_context_t) -> libc::c } #[no_mangle] -pub unsafe extern "C" fn dc_run(context: *mut dc_context_t) { +pub unsafe extern "C" fn dc_context_run(context: *mut dc_context_t, cb: Option) { if context.is_null() { eprintln!("ignoring careless call to dc_run()"); return; } let ffi_context = &*context; - ffi_context.with_inner(|ctx| ctx.run()).unwrap_or(()) + ffi_context + .with_inner(|ctx| { + ctx.run(|_ctx, event| { + translate_cb(ffi_context, cb, event); + }) + }) + .unwrap_or(()) +} + +/// Translates the callback from the rust style to the C-style version. +unsafe fn translate_cb(ctx: &ContextWrapper, ffi_cb: Option, event: Event) { + if let Some(ffi_cb) = ffi_cb { + let event_id = event.as_id(); + match event { + Event::Info(msg) + | Event::SmtpConnected(msg) + | Event::ImapConnected(msg) + | Event::SmtpMessageSent(msg) + | Event::ImapMessageDeleted(msg) + | Event::ImapMessageMoved(msg) + | Event::ImapFolderEmptied(msg) + | Event::NewBlobFile(msg) + | Event::DeletedBlobFile(msg) + | Event::Warning(msg) + | Event::Error(msg) + | Event::ErrorNetwork(msg) + | Event::ErrorSelfNotInGroup(msg) => { + let data2 = CString::new(msg).unwrap_or_default(); + ffi_cb(ctx, event_id, 0, data2.as_ptr() as uintptr_t); + } + Event::MsgsChanged { chat_id, msg_id } + | Event::IncomingMsg { chat_id, msg_id } + | Event::MsgDelivered { chat_id, msg_id } + | Event::MsgFailed { chat_id, msg_id } + | Event::MsgRead { chat_id, msg_id } => { + ffi_cb( + ctx, + event_id, + chat_id.to_u32() as uintptr_t, + msg_id.to_u32() as uintptr_t, + ); + } + Event::ChatModified(chat_id) => { + ffi_cb(ctx, event_id, chat_id.to_u32() as uintptr_t, 0); + } + Event::ContactsChanged(id) | Event::LocationChanged(id) => { + let id = id.unwrap_or_default(); + ffi_cb(ctx, event_id, id as uintptr_t, 0); + } + Event::ConfigureProgress(progress) | Event::ImexProgress(progress) => { + ffi_cb(ctx, event_id, progress as uintptr_t, 0); + } + Event::ImexFileWritten(file) => { + let data1 = file.to_c_string().unwrap_or_default(); + ffi_cb(ctx, event_id, data1.as_ptr() as uintptr_t, 0); + } + Event::SecurejoinInviterProgress { + contact_id, + progress, + } + | Event::SecurejoinJoinerProgress { + contact_id, + progress, + } => { + ffi_cb( + ctx, + event_id, + contact_id as uintptr_t, + progress as uintptr_t, + ); + } + Event::SecurejoinMemberAdded { + chat_id, + contact_id, + } => { + ffi_cb( + ctx, + event_id, + chat_id.to_u32() as uintptr_t, + contact_id as uintptr_t, + ); + } + } + } } #[no_mangle] -pub unsafe extern "C" fn dc_shutdown(context: *mut dc_context_t) { +pub unsafe extern "C" fn dc_context_shutdown(context: *mut dc_context_t) { if context.is_null() { eprintln!("ignoring careless call to dc_shutdown()"); return; diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 3e704286f..c83843422 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -38,7 +38,7 @@ class Account(object): :param debug: turn on debug logging for events. """ self._dc_context = ffi.gc( - lib.dc_context_new(lib.py_dc_callback, ffi.NULL, as_dc_charpointer(os_name)), + lib.dc_context_new(ffi.NULL, as_dc_charpointer(os_name)), _destroy_dc_context, ) if eventlogging: @@ -566,7 +566,7 @@ class IOThreads: t.start() def stop(self, wait=False): - lib.dc_shutdown(self._dc_context) + lib.dc_context_shutdown(self._dc_context) if wait: for name, thread in self._name2thread.items(): @@ -575,7 +575,7 @@ class IOThreads: def dc_thread_run(self): self._log_event("py-bindings-info", 0, "DC THREAD START") - lib.dc_run(self._dc_context) + lib.dc_context_run(self._dc_context, lib.py_dc_callback) self._log_event("py-bindings-info", 0, "INBOX THREAD FINISHED") diff --git a/src/context.rs b/src/context.rs index f76d698f3..d301bbafd 100644 --- a/src/context.rs +++ b/src/context.rs @@ -8,6 +8,7 @@ use std::sync::{ Arc, Condvar, Mutex, RwLock, }; +use crossbeam::channel::select; use crossbeam::channel::{bounded, unbounded, Receiver, Sender}; use crate::chat::*; @@ -188,10 +189,9 @@ impl Context { where F: Fn(&Context, Event) -> () + Send + Sync, { - // TODO: ensure this can be only called once. - - use crossbeam::channel::select; - self.is_running.store(true, Ordering::Relaxed); + if self.is_running.swap(true, Ordering::Relaxed) { + panic!("Run can only be called once"); + } crossbeam::scope(|s| { let imap_handle = s.spawn(|_| loop { @@ -249,7 +249,10 @@ impl Context { /// Stop the run loop. Blocks until all threads have shutdown. pub fn shutdown(&self) { - self.is_running.store(false, Ordering::Relaxed); + if !self.is_running.swap(false, Ordering::Relaxed) { + // already shutdown + return; + } self.shutdown_sender.send(()).unwrap(); job::interrupt_inbox_idle(self); @@ -258,7 +261,6 @@ impl Context { job::interrupt_smtp_idle(self); } - // connect pub fn configure(&self) { if self.has_ongoing() { warn!(self, "There is already another ongoing process running.");