11// Conserve backup system.
2- // Copyright 2017, 2018, 2019, 2020 Martin Pool.
2+ // Copyright 2017-2025 Martin Pool.
33
44// This program is free software; you can redistribute it and/or modify
55// it under the terms of the GNU General Public License as published by
@@ -45,6 +45,11 @@ pub struct GarbageCollectionLock {
4545 /// Last band id present when the guard was created. May be None if
4646 /// there are no bands.
4747 band_id : Option < BandId > ,
48+
49+ /// Is the lock actually held?
50+ ///
51+ /// Present so that we can avoid dropping it a second time from Drop.
52+ held : bool ,
4853}
4954
5055/// Lock on an archive for gc, that excludes backups and gc by other processes.
@@ -70,7 +75,11 @@ impl GarbageCollectionLock {
7075 . transport ( )
7176 . write ( GC_LOCK , b"{}\n " , WriteMode :: CreateNew )
7277 . await ?;
73- Ok ( GarbageCollectionLock { archive, band_id } )
78+ Ok ( GarbageCollectionLock {
79+ archive,
80+ band_id,
81+ held : true ,
82+ } )
7483 }
7584
7685 /// Take a lock on an archive, breaking any existing gc lock.
@@ -107,7 +116,7 @@ impl GarbageCollectionLock {
107116 /// Explicitly release the lock.
108117 ///
109118 /// Awaiting the future will ensure that the lock is released.
110- pub async fn release ( self ) -> Result < ( ) > {
119+ pub async fn release ( mut self ) -> Result < ( ) > {
111120 trace ! ( "Releasing GC lock" ) ;
112121 self . archive
113122 . transport ( )
@@ -116,14 +125,20 @@ impl GarbageCollectionLock {
116125 . map_err ( |err| {
117126 error ! ( ?err, "Failed to delete GC lock" ) ;
118127 Error :: from ( err)
119- } )
128+ } ) ?;
129+ self . held = false ;
130+ Ok ( ( ) )
120131 }
121132}
122133
123134impl Drop for GarbageCollectionLock {
124135 fn drop ( & mut self ) {
125136 // The lock will, hopefully, be deleted soon after the lock is dropped,
126137 // and before the process exits.
138+ if !self . held {
139+ return ;
140+ }
141+ trace ! ( "Releasing GC lock from Drop" ) ;
127142 let transport = self . archive . transport ( ) . clone ( ) ;
128143 tokio:: task:: spawn ( async move {
129144 transport. remove_file ( GC_LOCK ) . await . inspect_err ( |err| {
0 commit comments