@@ -29,7 +29,11 @@ export type WrappedWorkerConnectionOptions<Config extends ResolvedWebSQLOpenOpti
29
29
export class WorkerWrappedAsyncDatabaseConnection < Config extends ResolvedWebSQLOpenOptions = ResolvedWebSQLOpenOptions >
30
30
implements AsyncDatabaseConnection
31
31
{
32
- constructor ( protected options : WrappedWorkerConnectionOptions < Config > ) { }
32
+ protected lockAbortController : AbortController ;
33
+
34
+ constructor ( protected options : WrappedWorkerConnectionOptions < Config > ) {
35
+ this . lockAbortController = new AbortController ( ) ;
36
+ }
33
37
34
38
protected get baseConnection ( ) {
35
39
return this . options . baseConnection ;
@@ -44,6 +48,45 @@ export class WorkerWrappedAsyncDatabaseConnection<Config extends ResolvedWebSQLO
44
48
*/
45
49
async shareConnection ( ) : Promise < SharedConnectionWorker > {
46
50
const { identifier, remote } = this . options ;
51
+ /**
52
+ * Hold a navigator lock in order to avoid features such as Chrome's frozen tabs,
53
+ * or Edge's sleeping tabs from pausing the thread for this connection.
54
+ * This promise resolves once a lock is obtained.
55
+ * This lock will be held as long as this connection is open.
56
+ * The `shareConnection` method should not be called on multiple tabs concurrently.
57
+ */
58
+ await new Promise < void > ( ( resolve , reject ) =>
59
+ navigator . locks
60
+ . request (
61
+ `shared-connection-${ this . options . identifier } ` ,
62
+ {
63
+ signal : this . lockAbortController . signal
64
+ } ,
65
+ async ( ) => {
66
+ resolve ( ) ;
67
+
68
+ // Free the lock when the connection is already closed.
69
+ if ( this . lockAbortController . signal . aborted ) {
70
+ return ;
71
+ }
72
+
73
+ // Hold the lock while the shared connection is in use.
74
+ await new Promise < void > ( ( releaseLock ) => {
75
+ this . lockAbortController . signal . addEventListener ( 'abort' , ( ) => {
76
+ releaseLock ( ) ;
77
+ } ) ;
78
+ } ) ;
79
+ }
80
+ )
81
+ // We aren't concerned with abort errors here
82
+ . catch ( ( ex ) => {
83
+ if ( ex . name == 'AbortError' ) {
84
+ resolve ( ) ;
85
+ } else {
86
+ reject ( ex ) ;
87
+ }
88
+ } )
89
+ ) ;
47
90
48
91
const newPort = await remote [ Comlink . createEndpoint ] ( ) ;
49
92
return { port : newPort , identifier } ;
@@ -58,6 +101,8 @@ export class WorkerWrappedAsyncDatabaseConnection<Config extends ResolvedWebSQLO
58
101
}
59
102
60
103
async close ( ) : Promise < void > {
104
+ // Abort any pending lock requests.
105
+ this . lockAbortController . abort ( ) ;
61
106
await this . baseConnection . close ( ) ;
62
107
this . options . remote [ Comlink . releaseProxy ] ( ) ;
63
108
this . options . onClose ?.( ) ;
0 commit comments