1
1
use std:: collections:: BTreeMap ;
2
2
use std:: io:: { self , BufRead , Read , Write } ;
3
3
use std:: path:: { Path , PathBuf } ;
4
- use std:: process:: { Command , ExitStatus , Output } ;
5
- use std:: sync:: atomic:: { AtomicBool , Ordering } ;
4
+ use std:: process:: { Command , ExitStatus } ;
6
5
use std:: { env, fs, time} ;
7
6
8
7
use eyre:: Context ;
@@ -14,80 +13,9 @@ use crate::errors::Result;
14
13
use crate :: extensions:: CommandExt ;
15
14
use crate :: file:: { self , PathExt , ToUtf8 } ;
16
15
use crate :: rustc:: { self , QualifiedToolchain , VersionMetaExt } ;
17
- use crate :: shell:: { ColorChoice , MessageInfo , Stream , Verbosity } ;
16
+ use crate :: shell:: { MessageInfo , Stream } ;
17
+ use crate :: temp;
18
18
use crate :: TargetTriple ;
19
- use crate :: { temp, OutputExt } ;
20
-
21
- // the mount directory for the data volume.
22
- pub const MOUNT_PREFIX : & str = "/cross" ;
23
- // the prefix used when naming volumes
24
- pub const VOLUME_PREFIX : & str = "cross-" ;
25
- // default timeout to stop a container (in seconds)
26
- pub const DEFAULT_TIMEOUT : u32 = 2 ;
27
- // instant kill in case of a non-graceful exit
28
- pub const NO_TIMEOUT : u32 = 0 ;
29
-
30
- // we need to specify drops for the containers, but we
31
- // also need to ensure the drops are called on a
32
- // termination handler. we use an atomic bool to ensure
33
- // that the drop only gets called once, even if we have
34
- // the signal handle invoked multiple times or it fails.
35
- pub ( crate ) static mut CONTAINER : Option < DeleteContainer > = None ;
36
- pub ( crate ) static mut CONTAINER_EXISTS : AtomicBool = AtomicBool :: new ( false ) ;
37
-
38
- // it's unlikely that we ever need to erase a line in the destructors,
39
- // and it's better than keep global state everywhere, or keeping a ref
40
- // cell which could have already deleted a line
41
- pub ( crate ) struct DeleteContainer ( Engine , String , u32 , ColorChoice , Verbosity ) ;
42
-
43
- impl Drop for DeleteContainer {
44
- fn drop ( & mut self ) {
45
- // SAFETY: safe, since guarded by a thread-safe atomic swap.
46
- unsafe {
47
- if CONTAINER_EXISTS . swap ( false , Ordering :: SeqCst ) {
48
- let mut msg_info = MessageInfo :: new ( self . 3 , self . 4 ) ;
49
- container_stop ( & self . 0 , & self . 1 , self . 2 , & mut msg_info) . ok ( ) ;
50
- container_rm ( & self . 0 , & self . 1 , & mut msg_info) . ok ( ) ;
51
- }
52
- }
53
- }
54
- }
55
-
56
- #[ derive( Debug , PartialEq , Eq ) ]
57
- pub enum ContainerState {
58
- Created ,
59
- Running ,
60
- Paused ,
61
- Restarting ,
62
- Dead ,
63
- Exited ,
64
- DoesNotExist ,
65
- }
66
-
67
- impl ContainerState {
68
- pub fn new ( state : & str ) -> Result < Self > {
69
- match state {
70
- "created" => Ok ( ContainerState :: Created ) ,
71
- "running" => Ok ( ContainerState :: Running ) ,
72
- "paused" => Ok ( ContainerState :: Paused ) ,
73
- "restarting" => Ok ( ContainerState :: Restarting ) ,
74
- "dead" => Ok ( ContainerState :: Dead ) ,
75
- "exited" => Ok ( ContainerState :: Exited ) ,
76
- "" => Ok ( ContainerState :: DoesNotExist ) ,
77
- _ => eyre:: bail!( "unknown container state: got {state}" ) ,
78
- }
79
- }
80
-
81
- #[ must_use]
82
- pub fn is_stopped ( & self ) -> bool {
83
- matches ! ( self , Self :: Exited | Self :: DoesNotExist )
84
- }
85
-
86
- #[ must_use]
87
- pub fn exists ( & self ) -> bool {
88
- !matches ! ( self , Self :: DoesNotExist )
89
- }
90
- }
91
19
92
20
#[ derive( Debug , Clone ) ]
93
21
enum VolumeId {
@@ -101,50 +29,12 @@ enum VolumeId {
101
29
// commands while the container is cleaning up.
102
30
macro_rules! bail_container_exited {
103
31
( ) => { {
104
- if !container_exists ( ) {
32
+ if !Container :: exists_static ( ) {
105
33
eyre:: bail!( "container already exited due to signal" ) ;
106
34
}
107
35
} } ;
108
36
}
109
37
110
- pub fn create_container_deleter ( engine : Engine , container : String ) {
111
- // SAFETY: safe, since single-threaded execution.
112
- unsafe {
113
- CONTAINER_EXISTS . store ( true , Ordering :: Relaxed ) ;
114
- CONTAINER = Some ( DeleteContainer (
115
- engine,
116
- container,
117
- NO_TIMEOUT ,
118
- ColorChoice :: Never ,
119
- Verbosity :: Quiet ,
120
- ) ) ;
121
- }
122
- }
123
-
124
- pub fn drop_container ( is_tty : bool , msg_info : & mut MessageInfo ) {
125
- // SAFETY: safe, since single-threaded execution.
126
- unsafe {
127
- // relax the no-timeout and lack of output
128
- if let Some ( container) = & mut CONTAINER {
129
- if is_tty {
130
- container. 2 = DEFAULT_TIMEOUT ;
131
- }
132
- container. 3 = msg_info. color_choice ;
133
- container. 4 = msg_info. verbosity ;
134
- }
135
- CONTAINER = None ;
136
- }
137
- }
138
-
139
- fn container_exists ( ) -> bool {
140
- // SAFETY: safe, not mutating an atomic bool
141
- // this can be more relaxed: just used to ensure
142
- // that we don't make unnecessary calls, which are
143
- // safe even if executed, after we've signaled a
144
- // drop to our container.
145
- unsafe { CONTAINER_EXISTS . load ( Ordering :: Relaxed ) }
146
- }
147
-
148
38
fn subcommand_or_exit ( engine : & Engine , cmd : & str ) -> Result < Command > {
149
39
bail_container_exited ! ( ) ;
150
40
Ok ( subcommand ( engine, cmd) )
@@ -706,114 +596,6 @@ fn copy_volume_container_project(
706
596
Ok ( ( ) )
707
597
}
708
598
709
- fn run_and_get_status (
710
- engine : & Engine ,
711
- args : & [ & str ] ,
712
- msg_info : & mut MessageInfo ,
713
- ) -> Result < ExitStatus > {
714
- command ( engine)
715
- . args ( args)
716
- . run_and_get_status ( msg_info, true )
717
- }
718
-
719
- fn run_and_get_output (
720
- engine : & Engine ,
721
- args : & [ & str ] ,
722
- msg_info : & mut MessageInfo ,
723
- ) -> Result < Output > {
724
- command ( engine) . args ( args) . run_and_get_output ( msg_info)
725
- }
726
-
727
- pub fn volume_create (
728
- engine : & Engine ,
729
- volume : & str ,
730
- msg_info : & mut MessageInfo ,
731
- ) -> Result < ExitStatus > {
732
- run_and_get_status ( engine, & [ "volume" , "create" , volume] , msg_info)
733
- }
734
-
735
- pub fn volume_rm ( engine : & Engine , volume : & str , msg_info : & mut MessageInfo ) -> Result < ExitStatus > {
736
- run_and_get_status ( engine, & [ "volume" , "rm" , volume] , msg_info)
737
- }
738
-
739
- pub fn volume_exists ( engine : & Engine , volume : & str , msg_info : & mut MessageInfo ) -> Result < bool > {
740
- run_and_get_output ( engine, & [ "volume" , "inspect" , volume] , msg_info)
741
- . map ( |output| output. status . success ( ) )
742
- }
743
-
744
- pub fn existing_volumes (
745
- engine : & Engine ,
746
- toolchain : & QualifiedToolchain ,
747
- msg_info : & mut MessageInfo ,
748
- ) -> Result < Vec < String > > {
749
- let list = run_and_get_output (
750
- engine,
751
- & [
752
- "volume" ,
753
- "list" ,
754
- "--format" ,
755
- "{{.Name}}" ,
756
- "--filter" ,
757
- & format ! ( "name=^{VOLUME_PREFIX}{}" , toolchain) ,
758
- ] ,
759
- msg_info,
760
- ) ?
761
- . stdout ( ) ?;
762
-
763
- if list. is_empty ( ) {
764
- Ok ( vec ! [ ] )
765
- } else {
766
- Ok ( list. split ( '\n' ) . map ( ToOwned :: to_owned) . collect ( ) )
767
- }
768
- }
769
-
770
- pub fn container_stop (
771
- engine : & Engine ,
772
- container : & str ,
773
- timeout : u32 ,
774
- msg_info : & mut MessageInfo ,
775
- ) -> Result < ExitStatus > {
776
- run_and_get_status (
777
- engine,
778
- & [ "stop" , container, "--time" , & timeout. to_string ( ) ] ,
779
- msg_info,
780
- )
781
- }
782
-
783
- pub fn container_stop_default (
784
- engine : & Engine ,
785
- container : & str ,
786
- msg_info : & mut MessageInfo ,
787
- ) -> Result < ExitStatus > {
788
- // we want a faster timeout, since this might happen in signal
789
- // handler. our containers normally clean up pretty fast, it's
790
- // only without a pseudo-tty that they don't.
791
- container_stop ( engine, container, DEFAULT_TIMEOUT , msg_info)
792
- }
793
-
794
- // if stop succeeds without a timeout, this can have a spurious error
795
- // that is, if the container no longer exists. just silence this.
796
- pub fn container_rm (
797
- engine : & Engine ,
798
- container : & str ,
799
- msg_info : & mut MessageInfo ,
800
- ) -> Result < ExitStatus > {
801
- run_and_get_output ( engine, & [ "rm" , container] , msg_info) . map ( |output| output. status )
802
- }
803
-
804
- pub fn container_state (
805
- engine : & Engine ,
806
- container : & str ,
807
- msg_info : & mut MessageInfo ,
808
- ) -> Result < ContainerState > {
809
- let stdout = command ( engine)
810
- . args ( [ "ps" , "-a" ] )
811
- . args ( [ "--filter" , & format ! ( "name={container}" ) ] )
812
- . args ( [ "--format" , "{{.State}}" ] )
813
- . run_and_get_stdout ( msg_info) ?;
814
- ContainerState :: new ( stdout. trim ( ) )
815
- }
816
-
817
599
impl QualifiedToolchain {
818
600
pub fn unique_toolchain_identifier ( & self ) -> Result < String > {
819
601
// try to get the commit hash for the currently toolchain, if possible
@@ -980,7 +762,7 @@ pub(crate) fn run(
980
762
}
981
763
982
764
// store first, since failing to non-existing container is fine
983
- create_container_deleter ( engine. clone ( ) , container. clone ( ) ) ;
765
+ Container :: create ( engine. clone ( ) , container. clone ( ) ) ? ;
984
766
docker. run_and_get_status ( msg_info, true ) ?;
985
767
986
768
// 4. copy all mounted volumes over
@@ -1228,7 +1010,7 @@ symlink_recurse \"${{prefix}}\"
1228
1010
. map_err :: < eyre:: ErrReport , _ > ( Into :: into) ?;
1229
1011
}
1230
1012
1231
- drop_container ( is_tty, msg_info) ;
1013
+ Container :: finish_static ( is_tty, msg_info) ;
1232
1014
1233
1015
status
1234
1016
}
0 commit comments