@@ -40,15 +40,6 @@ fn main() {
4040
4141/// The main entry point to execute the application.
4242pub fn run_command ( ctx : & Context , command : Command ) -> Result < ( ) > {
43- // We always try to acquire the mutex but it is only strictly necessary for
44- // the lock and source commands.
45- let _guard = match acquire_mutex ( ctx, ctx. config_dir ( ) ) {
46- Ok ( g) => Some ( g) ,
47- Err ( _) if !matches ! ( command, Command :: Lock | Command :: Source ) => None ,
48- Err ( err) => {
49- return Err ( err) . context ( "failed to acquire lock on config directory" ) ;
50- }
51- } ;
5243 let mut warnings = Vec :: new ( ) ;
5344 let result = match command {
5445 Command :: Init { shell } => init ( ctx, shell) ,
@@ -64,27 +55,11 @@ pub fn run_command(ctx: &Context, command: Command) -> Result<()> {
6455 result
6556}
6657
67- fn acquire_mutex ( ctx : & Context , path : & Path ) -> Result < fmutex:: Guard > {
68- match fmutex:: try_lock ( path) . with_context ( || format ! ( "failed to open `{}`" , path. display( ) ) ) ? {
69- Some ( g) => Ok ( g) ,
70- None => {
71- ctx. log_warning (
72- "Blocking" ,
73- & format ! (
74- "waiting for file lock on {}" ,
75- ctx. replace_home( path) . display( )
76- ) ,
77- ) ;
78- fmutex:: lock ( path)
79- . with_context ( || format ! ( "failed to acquire file lock `{}`" , path. display( ) ) )
80- }
81- }
82- }
83-
8458/// Executes the `init` subcommand.
8559///
8660/// Initialize a new config file.
8761fn init ( ctx : & Context , shell : Option < Shell > ) -> Result < ( ) > {
62+ let _guard = access ( ctx, Access :: W ) ;
8863 let path = ctx. config_file ( ) ;
8964 match path
9065 . metadata ( )
@@ -105,6 +80,7 @@ fn init(ctx: &Context, shell: Option<Shell>) -> Result<()> {
10580///
10681/// Add a new plugin to the config file.
10782fn add ( ctx : & Context , name : String , plugin : & EditPlugin ) -> Result < ( ) > {
83+ let _guard = access ( ctx, Access :: W ) ;
10884 let path = ctx. config_file ( ) ;
10985 let mut config = match EditConfig :: from_path ( path) {
11086 Ok ( config) => {
@@ -124,6 +100,7 @@ fn add(ctx: &Context, name: String, plugin: &EditPlugin) -> Result<()> {
124100///
125101/// Open up the config file in the default editor.
126102fn edit ( ctx : & Context ) -> Result < ( ) > {
103+ let _guard = access ( ctx, Access :: W ) ;
127104 let path = ctx. config_file ( ) ;
128105 let original_contents = match fs:: read_to_string ( path)
129106 . with_context ( || format ! ( "failed to read from `{}`" , path. display( ) ) )
@@ -152,6 +129,7 @@ fn edit(ctx: &Context) -> Result<()> {
152129///
153130/// Remove a plugin from the config file.
154131fn remove ( ctx : & Context , name : String ) -> Result < ( ) > {
132+ let _guard = access ( ctx, Access :: W ) ;
155133 let path = ctx. config_file ( ) ;
156134 let mut config = EditConfig :: from_path ( path) ?;
157135 ctx. log_header ( "Loaded" , path) ;
@@ -191,6 +169,8 @@ fn init_config(ctx: &Context, shell: Option<Shell>, path: &Path, err: Error) ->
191169///
192170/// Install the plugins sources and generate the lock file.
193171fn lock ( ctx : & Context , warnings : & mut Vec < Error > ) -> Result < ( ) > {
172+ let _guard = access ( ctx, Access :: W ) ;
173+
194174 let mut locked = locked ( ctx, warnings) ?;
195175
196176 if let Some ( last) = locked. errors . pop ( ) {
@@ -216,36 +196,49 @@ fn source(ctx: &Context, warnings: &mut Vec<Error>) -> Result<()> {
216196 let mut to_path = true ;
217197
218198 let locked_config = if ctx. lock_mode . is_some ( ) || newer_than ( config_path, lock_path) {
199+ let _g = access ( ctx, Access :: W ) ?;
219200 locked ( ctx, warnings) ?
220201 } else {
221- match lock:: from_path ( lock_path) {
202+ let cfg = {
203+ let _g = access ( ctx, Access :: R ) ?;
204+ lock:: from_path ( lock_path)
205+ } ;
206+ match cfg {
222207 Ok ( locked_config) => {
223208 if locked_config. verify ( ctx) {
224209 to_path = false ;
225210 ctx. log_verbose_header ( "Unlocked" , lock_path) ;
226211 locked_config
227212 } else {
213+ let _g = access ( ctx, Access :: W ) ?;
228214 locked ( ctx, warnings) ?
229215 }
230216 }
231- Err ( _) => locked ( ctx, warnings) ?,
217+ Err ( _) => {
218+ let _g = access ( ctx, Access :: W ) ?;
219+ locked ( ctx, warnings) ?
220+ }
232221 }
233222 } ;
234223
235- let script = locked_config
236- . script ( ctx)
237- . context ( "failed to render source" ) ?;
224+ let script = {
225+ let _g = access ( ctx, Access :: R ) ?;
226+ let script = locked_config
227+ . script ( ctx)
228+ . context ( "failed to render source" ) ?;
238229
239- if to_path && locked_config. errors . is_empty ( ) {
240- locked_config
241- . to_path ( lock_path)
242- . context ( "failed to write lock file" ) ?;
243- ctx. log_header ( "Locked" , lock_path) ;
244- } else {
245- for err in & locked_config. errors {
246- ctx. log_error ( err) ;
230+ if to_path && locked_config. errors . is_empty ( ) {
231+ locked_config
232+ . to_path ( lock_path)
233+ . context ( "failed to write lock file" ) ?;
234+ ctx. log_header ( "Locked" , lock_path) ;
235+ } else {
236+ for err in & locked_config. errors {
237+ ctx. log_error ( err) ;
238+ }
247239 }
248- }
240+ script
241+ } ;
249242
250243 print ! ( "{script}" ) ;
251244 Ok ( ( ) )
@@ -269,3 +262,56 @@ fn locked(ctx: &Context, warnings: &mut Vec<Error>) -> Result<LockedConfig> {
269262 config:: clean ( ctx, warnings, & config) ?;
270263 lock:: config ( ctx, config)
271264}
265+
266+ #[ derive( Debug , Clone , Copy ) ]
267+ enum Access {
268+ R ,
269+ W ,
270+ }
271+
272+ fn access ( ctx : & Context , mode : Access ) -> Result < fmutex:: Guard < ' static > > {
273+ match mode {
274+ Access :: R => lock_read ( ctx) . context ( "failed to acquire exclusive lock on config directory" ) ,
275+ Access :: W => lock_write ( ctx) . context ( "failed to acquire shared lock on config directory" ) ,
276+ }
277+ }
278+
279+ fn lock_write ( ctx : & Context ) -> Result < fmutex:: Guard < ' static > > {
280+ let path = ctx. config_dir ( ) ;
281+ match fmutex:: try_lock_exclusive_path ( path)
282+ . with_context ( || format ! ( "failed to open `{}`" , path. display( ) ) ) ?
283+ {
284+ Some ( g) => Ok ( g) ,
285+ None => {
286+ ctx. log_warning (
287+ "Blocking" ,
288+ & format ! (
289+ "waiting for file lock on {}" ,
290+ ctx. replace_home( path) . display( )
291+ ) ,
292+ ) ;
293+ fmutex:: lock_exclusive_path ( path)
294+ . with_context ( || format ! ( "failed to acquire file lock `{}`" , path. display( ) ) )
295+ }
296+ }
297+ }
298+
299+ fn lock_read ( ctx : & Context ) -> Result < fmutex:: Guard < ' static > > {
300+ let path = ctx. config_dir ( ) ;
301+ match fmutex:: try_lock_shared_path ( path)
302+ . with_context ( || format ! ( "failed to open `{}`" , path. display( ) ) ) ?
303+ {
304+ Some ( g) => Ok ( g) ,
305+ None => {
306+ ctx. log_warning (
307+ "Blocking" ,
308+ & format ! (
309+ "waiting for file lock on {}" ,
310+ ctx. replace_home( path) . display( )
311+ ) ,
312+ ) ;
313+ fmutex:: lock_shared_path ( path)
314+ . with_context ( || format ! ( "failed to acquire file lock `{}`" , path. display( ) ) )
315+ }
316+ }
317+ }
0 commit comments