8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- // FIXME #3921. This is unsafe because linenoise uses global mutable
12
- // state without mutexes.
13
-
14
11
use std:: c_str:: ToCStr ;
15
12
use std:: libc:: { c_char, c_int} ;
16
- use std:: local_data;
17
- use std:: str ;
13
+ use std:: { local_data, str , rt } ;
14
+ use std:: unstable :: finally :: Finally ;
18
15
19
16
#[ cfg( stage0) ]
20
17
pub mod rustrt {
@@ -28,6 +25,9 @@ pub mod rustrt {
28
25
fn linenoiseHistoryLoad ( file : * c_char ) -> c_int ;
29
26
fn linenoiseSetCompletionCallback ( callback : * u8 ) ;
30
27
fn linenoiseAddCompletion ( completions : * ( ) , line : * c_char ) ;
28
+
29
+ fn rust_take_linenoise_lock ( ) ;
30
+ fn rust_drop_linenoise_lock ( ) ;
31
31
}
32
32
}
33
33
@@ -42,38 +42,53 @@ pub mod rustrt {
42
42
externfn ! ( fn linenoiseHistoryLoad( file: * c_char) -> c_int)
43
43
externfn ! ( fn linenoiseSetCompletionCallback( callback: extern "C" fn ( * i8 , * ( ) ) ) )
44
44
externfn ! ( fn linenoiseAddCompletion( completions: * ( ) , line: * c_char) )
45
+
46
+ externfn ! ( fn rust_take_linenoise_lock( ) )
47
+ externfn ! ( fn rust_drop_linenoise_lock( ) )
48
+ }
49
+
50
+ macro_rules! locked {
51
+ ( $expr: expr) => {
52
+ unsafe {
53
+ // FIXME #9105: can't use a static mutex in pure Rust yet.
54
+ rustrt:: rust_take_linenoise_lock( ) ;
55
+ let x = $expr;
56
+ rustrt:: rust_drop_linenoise_lock( ) ;
57
+ x
58
+ }
59
+ }
45
60
}
46
61
47
62
/// Add a line to history
48
- pub unsafe fn add_history ( line : & str ) -> bool {
63
+ pub fn add_history ( line : & str ) -> bool {
49
64
do line. with_c_str |buf| {
50
- rustrt:: linenoiseHistoryAdd ( buf) == 1 as c_int
65
+ ( locked ! ( rustrt:: linenoiseHistoryAdd( buf) ) ) == 1 as c_int
51
66
}
52
67
}
53
68
54
69
/// Set the maximum amount of lines stored
55
- pub unsafe fn set_history_max_len ( len : int ) -> bool {
56
- rustrt:: linenoiseHistorySetMaxLen ( len as c_int ) == 1 as c_int
70
+ pub fn set_history_max_len ( len : int ) -> bool {
71
+ ( locked ! ( rustrt:: linenoiseHistorySetMaxLen( len as c_int) ) ) == 1 as c_int
57
72
}
58
73
59
74
/// Save line history to a file
60
- pub unsafe fn save_history ( file : & str ) -> bool {
75
+ pub fn save_history ( file : & str ) -> bool {
61
76
do file. with_c_str |buf| {
62
- rustrt:: linenoiseHistorySave ( buf) == 1 as c_int
77
+ ( locked ! ( rustrt:: linenoiseHistorySave( buf) ) ) == 1 as c_int
63
78
}
64
79
}
65
80
66
81
/// Load line history from a file
67
- pub unsafe fn load_history ( file : & str ) -> bool {
82
+ pub fn load_history ( file : & str ) -> bool {
68
83
do file. with_c_str |buf| {
69
- rustrt:: linenoiseHistoryLoad ( buf) == 1 as c_int
84
+ ( locked ! ( rustrt:: linenoiseHistoryLoad( buf) ) ) == 1 as c_int
70
85
}
71
86
}
72
87
73
88
/// Print out a prompt and then wait for input and return it
74
- pub unsafe fn read ( prompt : & str ) -> Option < ~str > {
89
+ pub fn read ( prompt : & str ) -> Option < ~str > {
75
90
do prompt. with_c_str |buf| {
76
- let line = rustrt:: linenoise ( buf) ;
91
+ let line = locked ! ( rustrt:: linenoise( buf) ) ;
77
92
78
93
if line. is_null ( ) { None }
79
94
else { Some ( str:: raw:: from_c_str ( line) ) }
@@ -84,8 +99,13 @@ pub type CompletionCb = @fn(~str, @fn(~str));
84
99
85
100
static complete_key: local_data:: Key < @CompletionCb > = & local_data:: Key ;
86
101
87
- /// Bind to the main completion callback
88
- pub unsafe fn complete ( cb : CompletionCb ) {
102
+ /// Bind to the main completion callback.
103
+ ///
104
+ /// The completion callback should not call any `extra::rl` functions
105
+ /// other than the closure that it receives as its second
106
+ /// argument. Calling such a function will deadlock on the mutex used
107
+ /// to ensure that the calls are thread-safe.
108
+ pub fn complete ( cb : CompletionCb ) {
89
109
local_data:: set ( complete_key, @cb) ;
90
110
91
111
extern fn callback ( line : * c_char , completions : * ( ) ) {
@@ -95,12 +115,16 @@ pub unsafe fn complete(cb: CompletionCb) {
95
115
unsafe {
96
116
do cb ( str:: raw:: from_c_str ( line) ) |suggestion| {
97
117
do suggestion. with_c_str |buf| {
118
+ // This isn't locked, because `callback` gets
119
+ // called inside `rustrt::linenoise`, which
120
+ // *is* already inside the mutex, so
121
+ // re-locking would be a deadlock.
98
122
rustrt:: linenoiseAddCompletion ( completions, buf) ;
99
123
}
100
124
}
101
125
}
102
126
}
103
127
}
104
128
105
- rustrt:: linenoiseSetCompletionCallback ( callback) ;
129
+ locked ! ( rustrt:: linenoiseSetCompletionCallback( callback) ) ;
106
130
}
0 commit comments