-
Notifications
You must be signed in to change notification settings - Fork 33
Implement client logout and add logout button #432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Implement client logout and add logout button #432
Conversation
Signed-off-by: Alvin <[email protected]>
Due to the age of PR #293 and substantial changes in the codebase, resolving merge conflicts became quite challenging. I've contacted the original author, Guocork, who also suggested creating a new PR. Therefore, I've created a fresh PR implementing the same functionality with updated code and comprehensive testing. Could you please review this new implementation? |
Signed-off-by: Alvin <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Alvin, great start on this feature!
I left some comments below. In general, make sure to name things carefully --- try to give variables and types very specific names that cannot be misinterpreted. (I know naming is hard, so it'll take some time to get that right. No worries.)
Thank you for the review. I will try to improve the naming conventions and continue working on it. |
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
273f1f1
to
734536b
Compare
Signed-off-by: Alvin <[email protected]>
As I said on the Robrix matrix channel, I don't think re-logging in should restore the dock state by default. Generally, when logging out, most users would expect that the app be "reset" to its default state. However, in the future, we could show a prompt after log-in asking the user if they want to restore their previous dock/UI layout. |
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Currently, the logout function still logs panic messages, but these do not cause the robrix program to crash.
This is because the matrix SDK does not implement a graceful shutdown mechanism for deadpool-runtime. As a result, after the main loop of robrix’s tokio runtime exits, deadpool-runtime continues to run. Eventually, when robrix is terminated with ctrl-c, crash messages will appear in the logs:
@kevinaboos Do we need to report this issue in the matrix repository? update: After the refactoring, these exceptions should all be resolved, so there’s no need to report them upstream. |
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
Signed-off-by: Alvin <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, the code looks quite nice, especially the detailed comments and structure in the logout state machine. Nice!
However, I have serious concerns about the approach of leaking a bunch of huge states by mem::forget
ting them — that cannot possibly be the best solution. I left some comments about that and other issues, please let me know more about this issue so we can brainstorm an alternative solution.
@@ -128,7 +129,7 @@ live_design! { | |||
// However, the DesktopButton widget doesn't support drawing a background color yet, | |||
// so these colors are the colors of the icon itself, not the background highlight. | |||
// When it supports that, we will keep the icon color always black, | |||
// and change the background color instead based on the above colors. | |||
// and change the background color instead bssed on instead e colors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strange typo here. Please undo.
// Immediately take and leak all resources to prevent any destructors from running | ||
// This is a controlled leak at shutdown to avoid the deadpool panic | ||
|
||
// Take the runtime first and leak it | ||
if let Some(runtime) = TOKIO_RUNTIME.lock().unwrap().take() { | ||
std::mem::forget(runtime); | ||
log!("Leaked tokio runtime to prevent destructor"); | ||
} | ||
|
||
// Take and leak the client | ||
if let Some(client) = CLIENT.lock().unwrap().take() { | ||
std::mem::forget(client); | ||
log!("Leaked client to prevent destructor"); | ||
} | ||
|
||
// Take and leak the sync service | ||
if let Some(sync_service) = SYNC_SERVICE.lock().unwrap().take() { | ||
std::mem::forget(sync_service); | ||
log!("Leaked sync service to prevent destructor"); | ||
} | ||
|
||
// Take and leak the request sender | ||
if let Some(sender) = REQUEST_SENDER.lock().unwrap().take() { | ||
std::mem::forget(sender); | ||
log!("Leaked request sender to prevent destructor"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, but leaking a bunch of states is not a solution that I will accept.
What's the root cause here? Alex mentioned something about a Matrix SDK issue regarding deadpool, but I'm not really sure what that means. Please elaborate why this is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kevinaboos This intentional memory leak occurs because, when canceling asynchronous tasks during logout, the deadpool in the matrix SDK cannot be cancelled. As a result, when the robrix application is closed, a panic occurs, causing a crash and an exception dialog that affects the user experience. However, this intentional leak only happens during the shutdown process of robrix. In other words, as long as the user does not close the robrix program, the intentional leak will not occur.
Therefore, this intentional leak is a last-resort measure to preserve user experience when there is no other solution (at least, no proper way to destruct deadpool has been found so far). I think that after the program exits, an intentional leak shouldn’t be a problem, right? After all, the memory should be reclaimed by the system once the program is closed.
|
||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lots of whitespace
} | |
} |
self.label(id!(message)).set_text(cx, message); | ||
} | ||
|
||
fn reset_state(&mut self, cx: &mut Cx) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
due to a Makepad bug, in this function, you also need to call reset_hover()
on every button in the modal.
/// Clears the user profile cache. | ||
/// This is useful during shutdown to prevent thread-local destructors | ||
/// from running after the tokio runtime has been shut down. | ||
pub fn clear_cache() { | ||
USER_PROFILE_CACHE.with_borrow_mut(|cache| { | ||
cache.clear(); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't make any sense — can you explain?
- Thread-local destructors are good, we want them to run.
- Clearing the cache has nothing to do with thread-local destructors.
- This cache is only accessed from the main thread, and the main thread is not being exited/killed, so its destructors wouldn't run anyway.
Note that I do agree that we need to clear all caches upon logout, including the USER_PROFILE_CACHE
and AVATAR_CACHE
(please add that as well). I just don't understand the comments here about thread locals..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to clearing the user profile cache and the avatar cache, you also need to clear the list of ALL_INVITED_ROOMS
here:
Lines 19 to 25 in 5d5bc20
thread_local! { | |
/// The list of all invited rooms, which is only tracked here | |
/// because the backend doesn't need to track any info about them. | |
/// | |
/// This must only be accessed by the main UI thread. | |
static ALL_INVITED_ROOMS: Rc<RefCell<HashMap<OwnedRoomId, InvitedRoomInfo>>> = Rc::new(RefCell::new(HashMap::new())); | |
} |
and the set of all timeline states here:
robrix/src/home/room_screen.rs
Lines 2781 to 2788 in 5d5bc20
thread_local! { | |
/// The global set of all timeline states, one entry per room. | |
/// | |
/// This is only useful when accessed from the main UI thread. | |
static TIMELINE_STATES: RefCell<BTreeMap<OwnedRoomId, TimelineUiState>> = const { | |
RefCell::new(BTreeMap::new()) | |
}; | |
} |
static ALL_JOINED_ROOMS: Mutex<BTreeMap<OwnedRoomId, JoinedRoomDetails>> = Mutex::new(BTreeMap::new()); | ||
pub(crate) static ALL_JOINED_ROOMS: Mutex<BTreeMap<OwnedRoomId, JoinedRoomDetails>> = Mutex::new(BTreeMap::new()); | ||
|
||
/// Information about all of the rooms that have been tombstoned. | ||
/// | ||
/// The map key is the **NEW** replacement room ID, and the value is the **OLD** tombstoned room ID. | ||
/// This allows us to quickly query if a newly-encountered room is a replacement for an old tombstoned room. | ||
static TOMBSTONED_ROOMS: Mutex<BTreeMap<OwnedRoomId, OwnedRoomId>> = Mutex::new(BTreeMap::new()); | ||
pub(crate) static TOMBSTONED_ROOMS: Mutex<BTreeMap<OwnedRoomId, OwnedRoomId>> = Mutex::new(BTreeMap::new()); | ||
|
||
/// The logged-in Matrix client, which can be freely and cheaply cloned. | ||
static CLIENT: OnceLock<Client> = OnceLock::new(); | ||
pub(crate) static CLIENT: Mutex<Option<Client>> = Mutex::new(None); | ||
|
||
pub fn get_client() -> Option<Client> { | ||
CLIENT.get().cloned() | ||
CLIENT.lock().unwrap().clone() | ||
} | ||
|
||
/// Returns the user ID of the currently logged-in user, if any. | ||
pub fn current_user_id() -> Option<OwnedUserId> { | ||
CLIENT.get().and_then(|c| | ||
CLIENT.lock().unwrap().as_ref().and_then(|c| | ||
c.session_meta().map(|m| m.user_id.clone()) | ||
) | ||
} | ||
|
||
/// The singleton sync service. | ||
static SYNC_SERVICE: OnceLock<SyncService> = OnceLock::new(); | ||
/// sync_service if build from the client, and is used to sync the client with the server. | ||
pub(crate) static SYNC_SERVICE: Mutex<Option<Arc<SyncService>>> = Mutex::new(None); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all of these static
states should not be public in any way. We only want the sliding sync module's logic to be aware of them and be able to access them.
If you just need a function that clears all of these upon logout, then add a function to this module and call it from your logout logic. There's no reason to make them public if that's all you need.
Closes #277. Replaces #293 and #494.
Based on PR #293, I found an error message during logout:
2024-12-24T07:58:55.514036Z ERROR matrix_sdk_ui::sync_service: Error while processing encryption in sync service: Something wrong happened in sliding sync: the server returned an error: [401 / M_UNKNOWN_TOKEN] Invalid access token passed.
Root cause:
The error is related to SYNC_SERVICE handling during logout process.
Changes: