11use crate :: error:: Result ;
22use chrono:: Utc ;
33use std:: {
4- cell:: RefCell ,
4+ cell:: { Cell , RefCell } ,
55 collections:: hash_map:: { Entry , HashMap } ,
66 ffi:: OsString ,
77 fs,
88 fs:: { File , OpenOptions } ,
99 io:: Write ,
1010 path:: Path ,
11+ sync:: Once ,
12+ thread:: JoinHandle ,
1113} ;
1214
15+ static ONCE : Once = Once :: new ( ) ;
16+
1317thread_local ! {
14- static FILE_MAP : RefCell <HashMap <OsString , File >> = RefCell :: new( HashMap :: new( ) ) ;
18+ static FILE_MAP : RefCell <HashMap <OsString , File >> = RefCell :: new( HashMap :: new( ) ) ; //on worker thread
19+ static SENDER : RefCell <Option <flume:: Sender <Box <( String , String , bool ) >>>> = RefCell :: new( None ) ; //on main thread
20+ static HANDLE : Cell <Option <JoinHandle <( ) >>> = Cell :: new( None ) ; //on main thread
1521}
1622
1723byond_fn ! ( fn log_write( path, data, ...rest) {
18- FILE_MAP . with( |cell| -> Result <( ) > {
19- // open file
20- let mut map = cell. borrow_mut( ) ;
21- let path = Path :: new( path as & str ) ;
22- let file = match map. entry( path. into( ) ) {
23- Entry :: Occupied ( elem) => elem. into_mut( ) ,
24- Entry :: Vacant ( elem) => elem. insert( open( path) ?) ,
25- } ;
26-
27- if rest. first( ) . map( |x| & * * x) == Some ( "false" ) {
28- // Write the data to the file with no accoutrements.
29- write!( file, "{}" , data) ?;
30- } else {
31- // write first line, timestamped
32- let mut iter = data. split( '\n' ) ;
33- if let Some ( line) = iter. next( ) {
34- write!( file, "[{}] {}\n " , Utc :: now( ) . format( "%F %T%.3f" ) , line) ?;
35- }
36-
37- // write remaining lines
38- for line in iter {
39- write!( file, " - {}\n " , line) ?;
40- }
41- }
42-
43- Ok ( ( ) )
44- } ) . err( )
24+ init_worker( ) ;
25+ SENDER . with( |sender| {
26+ _ = sender. borrow( ) . as_ref( ) . unwrap( ) . send( Box :: new(
27+ ( path. to_string( ) , data. to_string( ) , rest. first( ) . map( |x| & * * x) == Some ( "false" ) )
28+ ) )
29+ } ) ;
30+ Some ( "" )
4531} ) ;
4632
4733byond_fn ! (
4834 fn log_close_all( ) {
49- FILE_MAP . with( |cell| {
50- let mut map = cell. borrow_mut( ) ;
51- map. clear( ) ;
35+ SENDER . with( |cell| cell. replace( None ) ) ;
36+ HANDLE . with( |cell| {
37+ if let Some ( handle) = cell. replace( None ) {
38+ let _ = handle. join( ) ;
39+ } ;
5240 } ) ;
5341 Some ( "" )
5442 }
@@ -61,3 +49,58 @@ fn open(path: &Path) -> Result<File> {
6149
6250 Ok ( OpenOptions :: new ( ) . append ( true ) . create ( true ) . open ( path) ?)
6351}
52+ fn init_worker ( ) {
53+ ONCE . call_once ( || {
54+ let ( sender, receiver) = flume:: unbounded ( ) ;
55+ SENDER . with ( |cell| * cell. borrow_mut ( ) = Some ( sender) ) ;
56+ HANDLE . with ( |cell| {
57+ let handle = std:: thread:: spawn ( move || {
58+ loop {
59+ let packet = receiver. recv ( ) ;
60+
61+ if let Ok ( packet) = packet {
62+ let ( path, data, rest) = * packet;
63+ _ = FILE_MAP . with ( |cell| -> Result < ( ) > {
64+ // open file
65+ let mut map = cell. borrow_mut ( ) ;
66+ let path = Path :: new ( & path) ;
67+ let file = match map. entry ( path. into ( ) ) {
68+ Entry :: Occupied ( elem) => elem. into_mut ( ) ,
69+ Entry :: Vacant ( elem) => elem. insert ( open ( path) ?) ,
70+ } ;
71+
72+ let mut buffer = std:: io:: BufWriter :: new ( file) ;
73+
74+ if rest {
75+ // Write the data to the file with no accoutrements.
76+ write ! ( buffer, "{}" , data) ?;
77+ } else {
78+ // write first line, timestamped
79+ let mut iter = data. split ( '\n' ) ;
80+ if let Some ( line) = iter. next ( ) {
81+ write ! (
82+ buffer,
83+ "[{}] {}\n " ,
84+ Utc :: now( ) . format( "%F %T%.3f" ) ,
85+ line
86+ ) ?;
87+ }
88+
89+ // write remaining lines
90+ for line in iter {
91+ write ! ( buffer, " - {}\n " , line) ?;
92+ }
93+ }
94+
95+ Ok ( ( ) )
96+ } ) ;
97+ } else {
98+ FILE_MAP . with ( |cell| cell. borrow_mut ( ) . clear ( ) ) ;
99+ return ;
100+ }
101+ }
102+ } ) ;
103+ cell. set ( Some ( handle) ) ;
104+ } ) ;
105+ } ) ;
106+ }
0 commit comments