VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/wayland-helper.cpp

Last change on this file was 101880, checked in by vboxsync, 6 months ago

Additions: X11/Wayland: scm fix, bugref:10194.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.3 KB
Line 
1/* $Id: wayland-helper.cpp 101880 2023-11-06 15:49:29Z vboxsync $ */
2/** @file
3 * Guest Additions - Common code for Wayland Desktop Environment helpers.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <iprt/env.h>
29#include <iprt/asm.h>
30#include <iprt/assert.h>
31#include <iprt/thread.h>
32#include <iprt/err.h>
33#include <iprt/time.h>
34
35#include "VBoxClient.h"
36#include "wayland-helper.h"
37
38static const char *g_pcszSessionDescClipardCopyToGuest = "Copy clipboard to the guest";
39static const char *g_pcszSessionDescClipardAnnounceToHost = "Announce and copy clipboard to the host";
40static const char *g_pcszSessionDescClipardCopyToHost = "Copy clipboard to the host";
41
42/**
43 * Interaction between VBoxClient and Wayland environment implies
44 * a sequential data exchange between both parties. In order to
45 * temporary store and protect this data, we put it into a Session.
46
47 * Session can be started, joined or ended.
48
49 * Each time when either host (via VBoxClient) or Wayland client
50 * initiates a new data transfer (such as clipboard sharing or drag-n-drop
51 * operation) a new session should be started. Due to current implementation,
52 * no more than one session can run in the same time. Therefore, before
53 * starting new session, previous one needs to be ended.
54
55 * When either VBoxClient or Wayland thread needs to access session data,
56 * it need to join session at first. Multiple threads can join the same
57 * session in parallel.
58
59 * When sequence of operations which were required to transfer data to
60 * either side is complete, session should be ended.
61 *
62 * Session has the following attributes:
63 *
64 * STATE - synchronization point which is used in order to prevent
65 * access to session data when it is not allowed (see session
66 * states description).
67 * TYPE - unique attribute which should be used for sanity checks
68 * when accessing session data.
69 * DESCRIPTION - a text attribute which is mostly used for logging purposes
70 * when monitoring multiple sequential sessions flow.
71 * REFERENCE COUNT - attribute which shows a number of session users who currently
72 * joined a session.
73 *
74 * Session state represents synchronization point when session data needs to
75 * be accessed. It can have permanent or intermediate value. The following states
76 * are defined: IDLE, STARTING, STARTED and TERMINATING.
77 *
78 * Default state is IDLE. It is set when session is just initialized or when
79 * it is ended.
80 *
81 * When user starts a new session, its state will be atomically switched from
82 * IDLE to STARTING. Then, initialization callback, provided by user, will be invoked.
83 * And, finally, state will be atomically transitioned from STARTING to STARTED.
84 * Intermediate state STARTING will ensure that no other user will be allowed to
85 * start, join or end session during this initialization step.
86 *
87 * User is only allowed to join session when it is in STARTED state. Once
88 * session is joined, its reference counted will be increased by 1, provided
89 * user callback will be called and, finally, reference counter will be dropped by 1.
90 *
91 * When user ends session, at first, its state will be atomically transitioned
92 * from STARTED to TERMINATING ensuring that no other user can start, join or end
93 * session in the same time. Then session will wait until all the users who joined
94 * the session are left by looking into session's reference counter. After this, it
95 * will invoke provided by user termination callback and atomically transition its state
96 * from TERMINATING to IDLE, allowing to start a new session.
97 */
98
99/**
100 * Try atomically enter session state within time interval.
101 *
102 * @returns IPRT status code.
103 * @param pSessionState Session state.
104 * @param to New session state ID to switch to.
105 * @param from Old session state ID which will be
106 * atomically compared with the current
107 * state before switching.
108 * @param u64TimeoutMs Operation timeout.
109 */
110static int vbcl_wayland_session_enter_state(volatile vbcl_wl_session_state_t *pSessionState,
111 vbcl_wl_session_state_t to, vbcl_wl_session_state_t from, uint64_t u64TimeoutMs)
112{
113 bool fRc = false;
114 uint64_t tsStart = RTTimeMilliTS();
115
116 while ( !(fRc = ASMAtomicCmpXchgU8((volatile uint8_t *)pSessionState, to, from))
117 && (RTTimeMilliTS() - tsStart) < u64TimeoutMs)
118 {
119 RTThreadSleep(VBCL_WAYLAND_RELAX_INTERVAL_MS);
120 }
121
122 return fRc ? VINF_SUCCESS : VERR_RESOURCE_BUSY;
123}
124
125
126
127RTDECL(void) vbcl_wayland_session_init(vbcl_wl_session_t *pSession)
128{
129 AssertPtrReturnVoid(pSession);
130
131 /* Reset internal data to default values. */
132 ASMAtomicWriteU32(&pSession->u32Magic, VBCL_WAYLAND_SESSION_MAGIC);
133 ASMAtomicWriteU8((volatile uint8_t *)&pSession->enmState, VBCL_WL_SESSION_STATE_IDLE);
134
135 pSession->enmType = VBCL_WL_SESSION_TYPE_INVALID;
136 pSession->pcszDesc = NULL;
137
138 ASMAtomicWriteU32(&pSession->cUsers, 0);
139}
140
141RTDECL(int) vbcl_wayland_session_start(vbcl_wl_session_t *pSession,
142 vbcl_wl_session_type_t enmType,
143 PFNVBCLWLSESSIONCB pfnStart,
144 void *pvUser)
145{
146 int rc;
147 const char *pcszDesc;
148
149 /* Make sure mandatory parameters were provided. */
150 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
151 AssertPtrReturn(pfnStart, VERR_INVALID_PARAMETER);
152
153 /* Make sure session was initialized. */
154 AssertReturn(ASMAtomicReadU32(&pSession->u32Magic) == VBCL_WAYLAND_SESSION_MAGIC,
155 VERR_SEM_DESTROYED);
156
157 /* Set session description according to its type. */
158 if (enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST)
159 pcszDesc = g_pcszSessionDescClipardCopyToGuest;
160 else if (enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_ANNOUNCE_TO_HOST)
161 pcszDesc = g_pcszSessionDescClipardAnnounceToHost;
162 else if (enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST)
163 pcszDesc = g_pcszSessionDescClipardCopyToHost;
164 else
165 pcszDesc = NULL;
166
167 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
168 VBCL_WL_SESSION_STATE_STARTING,
169 VBCL_WL_SESSION_STATE_IDLE,
170 RT_MS_1SEC);
171 if (RT_SUCCESS(rc))
172 {
173 int rcStart;
174
175 /* Make sure nobody is using the session. */
176 Assert(ASMAtomicReadU32(&pSession->cUsers) == 0);
177
178 /* Set session type. */
179 pSession->enmType = enmType;
180 /* Set session description. */
181 pSession->pcszDesc = pcszDesc;
182 /* Reset reference counter. */
183 ASMAtomicWriteU32(&pSession->cUsers, 1);
184
185 rcStart = pfnStart(enmType, pvUser);
186
187 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
188 VBCL_WL_SESSION_STATE_STARTED,
189 VBCL_WL_SESSION_STATE_STARTING,
190 RT_MS_1SEC);
191
192 VBClLogVerbose(2, "session start [%s], rc=%Rrc, rcStart=%Rrc\n",
193 pSession->pcszDesc, rc, rcStart);
194
195 if (RT_SUCCESS(rc))
196 rc = rcStart;
197 }
198 else
199 VBClLogError("cannot start session [%s], another session [%s] is currently in progress, rc=%Rrc\n",
200 pcszDesc, pSession->pcszDesc, rc);
201
202 return rc;
203}
204
205RTDECL(int) vbcl_wayland_session_join_ex(vbcl_wl_session_t *pSession,
206 PFNVBCLWLSESSIONCB pfnJoin, void *pvUser,
207 const char *pcszCallee)
208{
209 int rc;
210
211 /* Make sure mandatory parameters were provided. */
212 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
213 AssertPtrReturn(pfnJoin, VERR_INVALID_PARAMETER);
214
215 /* Make sure session was initialized. */
216 AssertReturn(ASMAtomicReadU32(&pSession->u32Magic) == VBCL_WAYLAND_SESSION_MAGIC,
217 VERR_SEM_DESTROYED);
218
219 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
220 VBCL_WL_SESSION_STATE_STARTED,
221 VBCL_WL_SESSION_STATE_STARTED,
222 RT_MS_1SEC);
223 if (RT_SUCCESS(rc))
224 {
225 uint32_t cUsers;
226 const char *pcszDesc = pSession->pcszDesc;
227
228 /* Take session reference. */
229 cUsers = ASMAtomicIncU32(&pSession->cUsers);
230 VBClLogVerbose(2, "session join [%s @ %s], cUsers=%u\n", pcszDesc, pcszCallee, cUsers);
231
232 /* Process callback while holding reference to session. */
233 rc = pfnJoin(pSession->enmType, pvUser);
234
235 /* Release session reference. */
236 cUsers = ASMAtomicDecU32(&pSession->cUsers);
237 VBClLogVerbose(2, "session leave [%s @ %s], cUsers=%u, rc=%Rrc\n",
238 pcszDesc, pcszCallee, cUsers, rc);
239
240 }
241 else
242 VBClLogError("cannot join session from %s, rc=%Rrc\n", pcszCallee, rc);
243
244 return rc;
245}
246
247RTDECL(int) vbcl_wayland_session_end(vbcl_wl_session_t *pSession,
248 PFNVBCLWLSESSIONCB pfnEnd, void *pvUser)
249{
250 int rc;
251
252 /* Make sure mandatory parameters were provided.. */
253 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
254
255 /* Make sure session was initialized. */
256 AssertReturn(ASMAtomicReadU32(&pSession->u32Magic) == VBCL_WAYLAND_SESSION_MAGIC,
257 VERR_SEM_DESTROYED);
258
259 /* Session not running? */
260 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
261 VBCL_WL_SESSION_STATE_IDLE,
262 VBCL_WL_SESSION_STATE_IDLE,
263 0 /* u64TimeoutMs */);
264 if (RT_SUCCESS(rc))
265 {
266 /* NOP. */
267 VBClLogVerbose(2, "session end: nothing to do, no active session is running\n");
268 }
269 else
270 {
271 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
272 VBCL_WL_SESSION_STATE_TERMINATING, VBCL_WL_SESSION_STATE_STARTED,
273 RT_MS_1SEC);
274 if (RT_SUCCESS(rc))
275 {
276 int rcEnd = VINF_SUCCESS;
277 const char *pcszDesc = pSession->pcszDesc;
278
279 /* Wait until for other callers to release session reference. */
280 while (ASMAtomicReadU32(&pSession->cUsers) > 1)
281 RTThreadSleep(VBCL_WAYLAND_RELAX_INTERVAL_MS);
282
283 if (RT_VALID_PTR(pfnEnd))
284 rcEnd = pfnEnd(pSession->enmType, pvUser);
285
286 pSession->enmType = VBCL_WL_SESSION_TYPE_INVALID;
287 pSession->pcszDesc = NULL;
288
289 ASMAtomicDecU32(&pSession->cUsers);
290
291 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
292 VBCL_WL_SESSION_STATE_IDLE, VBCL_WL_SESSION_STATE_TERMINATING,
293 RT_MS_1SEC);
294
295 VBClLogVerbose(2, "session end [%s], rc=%Rrc, rcEnd=%Rrc\n", pcszDesc, rc, rcEnd);
296
297 if (RT_SUCCESS(rc))
298 rc = rcEnd;
299 }
300 else
301 VBClLogError("cannot end session, rc=%Rrc\n", rc);
302 }
303
304 return rc;
305}
306
307RTDECL(bool) vbcl_wayland_session_is_started(vbcl_wl_session_t *pSession)
308{
309 /* Make sure mandatory parameters were provided. */
310 AssertPtrReturn(pSession, false);
311
312 /* Make sure session was initialized. */
313 AssertReturn(ASMAtomicReadU32(&pSession->u32Magic) == VBCL_WAYLAND_SESSION_MAGIC, false);
314
315 return RT_BOOL(ASMAtomicReadU8((volatile uint8_t *)&pSession->enmState) == VBCL_WL_SESSION_STATE_STARTED);
316}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use