Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MpiFileBuffer.cc
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // This file is part of the Rosetta software suite and is made available under license.
5 // The Rosetta software is developed by the contributing members of the Rosetta Commons consortium.
6 // (C) 199x-2009 Rosetta Commons participating institutions and developers.
7 // For more information, see http://www.rosettacommons.org/.
8 
9 /// @file protocols/jd2/MpiFileBuffer.hh
10 /// @brief header file for MPISilentFileJobOutputter class, part of August 2008 job distributor as planned at RosettaCon08
11 /// @detail this outputter will send silentstructs via MPI to dedicated node that will collect all structures
12 /// @author Oliver Lange olange@u.washington.edu
13 
15 #include <iostream>
16 #include <utility/io/mpistream.hh>
17 #include <utility/exit.hh>
18 #ifdef USEMPI
19 #include <utility/file/file_sys_util.hh>
20 #endif
21 
22 #include <basic/Tracer.hh>
23 #include <basic/MemTracer.hh>
24 // AUTO-REMOVED #include <iterator>
25  #include <ctime>
26 
28 #include <utility/vector1.hh>
29 
30 
31 namespace protocols {
32 namespace jd2 {
33 
34 using namespace core;
35 using namespace utility::io::mpi_stream;
36 
37 static basic::Tracer tr("protocols.jd2.MpiFileBuffer");
38 using basic::mem_tr;
39 
41  : buffer_rank_( file_buf_rank ),
42  last_channel_( 0 ),
43  bSlaveCanOpenFile_( true ),
44  bKeepFilesAlive_( true ),
45  seconds_to_keep_files_alive_( 100 ) {
46 
48  last_garbage_collection_ = time(NULL);
49 #ifdef USEMPI
50  int my_rank;
51  MPI_Comm_rank (MPI_COMM_WORLD, &my_rank );/* get current process id */
52  my_rank_ = my_rank;
53 #endif
54 
55 }
56 
57 Size const MPI_WIND_DOWN( 1001 );
58 Size const MPI_BLOCK_FILE( 1002 );
59 Size const MPI_RELEASE_FILE( 1003 );
60 Size const MPI_CLOSE_FILE( 1004 );
61 
62 #ifdef USEMPI
63 void MpiFileBuffer::receive_str( Size slave, Size size, std::string& line ) {
64  char *cbuf = new char[ size+1 ];
65  MPI_Status stat;
66  MPI_Recv( cbuf, size, MPI_CHAR, slave, MPI_STREAM_TAG, MPI_COMM_WORLD, &stat );
67  line.assign( cbuf, size );
68  delete[] cbuf;
69 }
70 #else
72 #endif
73 
75  while ( blocked_files_.size() ) {
76  release_file( blocked_files_.front() );
77  }
79 }
80 
82  time_t const now( time(NULL) );
83  //tr.Debug << "last garbage collection " << now-last_garbage_collection_ << " seconds ago" << std::endl;
84  if ( now-last_garbage_collection_ < 30 ) return;
85  tr.Debug << "garbage collection active..." << std::endl;
86  for ( GarbageList::iterator it=garbage_collector_.begin(); it!=garbage_collector_.end(); ) {
87  GarbageList::iterator to_erase( it );
88  ++it; //move forward now, because to_erase will be erased in "close_file"
89  tr.Debug << "marked " << to_erase->first << " " << now-to_erase->second << " seconds ago." << std::endl;
90  if ( now-to_erase->second > seconds_to_keep_files_alive_ ) {
91  int channel( to_erase->first );
92  SingleFileBufferOP buf = open_buffers_[ channel ];
93  if ( !buf ) {
94  garbage_collector_.erase( to_erase );
95  // this is now understood. some files can be closed directly via MPI_CLOSE_FILE (e.g., from ArchiveManager )
96  // to avoid this case I could call clear_channel_from_garbage_collector( channel_id ) in close_file()
97  // but then one has to be careful to not delete the iterator in this for-loop
98  continue;
99  }
100  if ( !buf->has_open_slaves() ) {
101  tr.Debug << "channel "<< channel
102  << " has no more open slaves... and has not been touched again --- close via garbage collector" << std::endl;
103  // garbage_collector_.erase( to_erase ); now removed from garbage_collector in close_file()
104  close_file( channel );
105  mem_tr << "closed_channel" << std::endl;
106  } else {
107  runtime_assert( false ); //shouldn't happen anymore
108  tr.Debug << "channel " << to_erase->first << " has open slaves again ... not closed, remove from closing list" << std::endl;
109  // garbage_collector_.erase( to_erase );
110  }
111  }
112  }
114 }
115 
117 #ifdef USEMPI
118  tr.Debug << "MpiFileBuffer " << (( my_rank_ != buffer_rank_ ) ? "not" : "" ) << " started on node " << my_rank_ << std::endl;
119  if ( my_rank_ != buffer_rank_ ) return;
120  MPI_Status stat;
121  bStop_ = false; //this might be changed from some where via msg.
122  while( !bStop_ ) {
123  int buf[ 4 ];
124  MPI_Recv( buf, 4, MPI_INT, MPI_ANY_SOURCE, MPI_STREAM_TAG, MPI_COMM_WORLD, &stat );
125  Size const msg_type( buf[ 2 ] );
126  Size const size( buf[ 1 ] );
127  Size const slave( buf[ 0 ] );
128  Size const channel_id( buf[ 3 ] );
129  if ( msg_type == MPI_STREAM_OPEN || msg_type == MPI_STREAM_OPEN_APPEND ) {
131  receive_str( slave, size, filename );
132  Size file_status;
133  open_channel( slave, filename, msg_type == MPI_STREAM_OPEN_APPEND, file_status );
134  } else if ( msg_type == MPI_STREAM_SEND ) {
135  std::string line;
136  receive_str( slave, size, line );
137  store_to_channel( slave, channel_id, line );
138  } else if ( msg_type == MPI_STREAM_CLOSE ) {
139  close_channel( slave, channel_id );
140  } else if ( msg_type == MPI_STREAM_FLUSH ) {
141  tr.Debug << "MPI_STREAM_FLUSH received" << std::endl;
142  flush_channel( slave, channel_id );
143  } else if ( msg_type == MPI_WIND_DOWN ) {
144  bStop_ = true;
145  } else if ( msg_type == MPI_BLOCK_FILE ) {
147  receive_str( slave, size, filename );
148  block_file( slave, filename );
149  } else if ( msg_type == MPI_STREAM_FILE_EXIST ) {
151  receive_str( slave, size, filename );
152  bool exist( is_open_channel( filename ) );
153  if ( !exist ) exist = utility::file::file_exists( filename );
154  int iexist( exist ? 1 : 0 );
155  MPI_Send( &iexist, 1, MPI_INT, slave, MPI_STREAM_TAG, MPI_COMM_WORLD );
156  } else if ( msg_type == MPI_CLOSE_FILE ) {
158  receive_str( slave, size, filename );
159  tr.Debug << "received MPI_CLOSE_FILE " <<filename<<" from Node" << slave << std::endl;
160  bool success = close_file( filename );
161  //send confirmation...
162  int closed = success ? 1 : 0;
163  MPI_Send( &closed, 1, MPI_INT, slave, MPI_STREAM_TAG, MPI_COMM_WORLD );
164  } else {
165  utility_exit_with_message( "unknown msg-id received in MpiFileBuffer.cc");
166  }
168  }
169 #endif
170 }
171 
172 
174 #ifdef USEMPI
175  if (my_rank_ == buffer_rank_ ) { bStop_=true; return; };
176  int buf[ 4 ];
177  buf[ 0 ] = my_rank_;
178  buf[ 1 ] = 0;
179  buf[ 2 ] = MPI_WIND_DOWN;
180  buf[ 3 ] = 0;
181  MPI_Send( &buf, 4, MPI_INT, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD );
182 #endif
183 }
184 
186  runtime_assert( my_rank_ == buffer_rank_ );
187  Filenames::const_iterator iter = open_files_.find( filename );
188  Size channel;
189 
190  if ( iter != open_files_.end() ) {
191  channel = iter->second;
192  SingleFileBufferOP buf = open_buffers_[ channel ];
193  runtime_assert( buf ); //consistent?
194  buf->block( from_node ); //closes-file, sends MPI signal back and forth and hangs until release, reopens file
195  tr.Debug << "block released... for file " << filename << std::endl;
196  } else {
197  tr.Warning << "file " << filename << " is not known to MpiFileBuffer " << std::endl;
198 #ifdef USEMPI
199  int status = 0;
200  tr.Debug << "send blocking confirmation... " << filename << std::endl;
201  MPI_Send( &status, 1, MPI_INT, from_node, MPI_STREAM_TAG, MPI_COMM_WORLD );
202 #endif
203  }
204 }
205 
206 void MpiFileBuffer::block_file( std::string const& MPI_ONLY(filename) ) {
207 #ifdef USEMPI
208  runtime_assert( buffer_rank_ != my_rank_ );
209  int buf[ 4 ];
210  buf[ 0 ] = my_rank_;
211  buf[ 1 ] = filename.size();
212  buf[ 2 ] = MPI_BLOCK_FILE;
213  buf[ 3 ] = 0;
214 
215  MPI_Send(buf, 4, MPI_INT, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD );
216  MPI_Send(const_cast<char*> (filename.data()), filename.size(), MPI_CHAR, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD );
217  tr.Debug << "wait for confirmation of block " << std::endl;
218  MPI_Status stat;
219  MPI_Recv( &buf, 1, MPI_INT, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD, &stat );//file is blocked when we receive this
220  if ( buf[ 0 ] == 1 ) {
221  tr.Debug << "block confirmed... " << std::endl;
222  blocked_files_.push_back( filename );
223  } else {
224  tr.Debug << "block not accepted ... " << std::endl;
225  }
226  // now do stuff and then say release_file
227 #endif
228 }
229 
230 
232  runtime_assert( buffer_rank_ != my_rank_ );
233  int buf[ 4 ];
234  buf[ 0 ] = my_rank_;
235  buf[ 1 ] = filename.size();
236  buf[ 2 ] = MPI_CLOSE_FILE;
237  buf[ 3 ] = 0;
238 #ifdef USEMPI
239 
240  MPI_Send(buf, 4, MPI_INT, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD );
241  MPI_Send(const_cast<char*> (filename.data()), filename.size(), MPI_CHAR, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD );
242 
243  MPI_Status stat;
244  MPI_Recv( &buf, 1, MPI_INT, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD, &stat );//file is blocked when we receive this
245 #endif
246  if ( buf[ 0 ] == 1 ) {
247  tr.Debug << "remote close confirmed... " << std::endl;
248  } else {
249  tr.Debug << "close not accepted ... " << std::endl;
250  }
251  return buf[ 0 ] != 0;
252  // now do stuff and then say release_file
253 
254 }
255 
256 
257 
259  //if this is a reference I get seqfault, since it might point into list where I erase from...
260  std::list< std::string >::iterator iter = find( blocked_files_.begin(), blocked_files_.end(), filename );
261  if ( iter != blocked_files_.end() ) {
262  blocked_files_.erase( iter );
263  runtime_assert( buffer_rank_ != my_rank_ );
264 #ifdef USEMPI
265  int buf[ 4 ];
266  buf[ 0 ] = my_rank_;
267  buf[ 1 ] = filename.size();
268  buf[ 2 ] = MPI_RELEASE_FILE;
269  buf[ 3 ] = 0;
270 //#ifdef USEMPI
271  tr.Debug << "release file " << filename << std::endl;
272  MPI_Send(buf, 4, MPI_INT, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD );
273  //don't send filename again....
274  //MPI_Send(const_cast<char*> (filename.data()), filename.size(), MPI_CHAR, buffer_rank_, MPI_STREAM_TAG, MPI_COMM_WORLD );
275 #endif
276  } else {
277  tr.Debug << filename << " is not blocked... release ignored " << std::endl;
278  }
279 }
280 
281 
283  Filenames::const_iterator iter = open_files_.find( filename );
284  //Size channel;
285  return ( iter != open_files_.end() );
286 }
287 
289  //remove from garbage_collector if its in there already
290  GarbageList::iterator giter = garbage_collector_.find( channel );
291  if ( giter != garbage_collector_.end() ) {
292  tr.Debug << "remove channel " << channel << " from garbage-collector list " << std::endl;
293  garbage_collector_.erase( giter );
294  }
295 }
296 
297 #ifdef USEMPI
298 void MpiFileBuffer::open_channel( Size slave, std::string const& filename, bool append, Size& status ) {
299  //find filename in our file-map...
300  tr.Debug << "open mpi-channel from slave-node " << slave << " for file: " << filename << std::endl;
301  Filenames::const_iterator iter = open_files_.find( filename );
302  Size channel;
303  if ( iter != open_files_.end() ) {
304  channel = iter->second;
305  SingleFileBufferOP buf = open_buffers_[ channel ];
306  runtime_assert( buf ); //consistent?
307  //buf->flush( slave );
308  tr.Debug << "channel exists already: " << channel << std::endl;
309  status = MPI_SUCCESS_APPEND;
310 
312  } else {
313  //new file
314  channel = ++last_channel_; //this might overrun eventually
315  runtime_assert( channel < 2147483647 /* 2^32 -1 */ );
316  open_files_.insert( Filenames::value_type( filename, channel ) );
317  open_buffers_.insert( Buffers::value_type( channel, generate_new_channel( filename, channel, append, status )) );
318  tr.Debug << "new channel established: " << channel << std::endl;
319  }
320  int send_buf[ 2 ];
321  send_buf[ 0 ] = channel;
322  send_buf[ 1 ] = status;
323  MPI_Send( &send_buf, 2, MPI_INT, slave, MPI_STREAM_TAG, MPI_COMM_WORLD );
324  if ( status == MPI_SUCCESS_NEW ) {
325  int buf[ 4 ];
326  MPI_Status stat;
327  MPI_Recv( buf, 4, MPI_INT, slave, MPI_STREAM_TAG, MPI_COMM_WORLD, &stat );
328  Size const msg_type( buf[ 2 ] );
329  Size const size( buf[ 1 ] );
330  Size const slave_id( buf[ 0 ] );
331  Size const channel_id( buf[ 3 ] );
332  tr.Debug << "header? : received: " << buf[ 0 ] << " " << buf[ 1 ] << " " << buf[ 2 ] << " " << buf[ 3 ] << std::endl;
333  runtime_assert( msg_type == MPI_STREAM_SEND && slave_id == slave );
334  std::string header;
335  receive_str( slave, size, header );
336  tr.Debug << header << std::endl;
337  open_buffers_[ channel ]->store_line( slave, channel, header );
338  open_buffers_[ channel ]->flush( slave ); //flush to write header first
339  }
340  //send file-descriptor out
341 }
342 #else
343 void MpiFileBuffer::open_channel( Size , std::string const&, bool, Size& status ) {
344  status = 0;
345 }
346 #endif
347 
348 
349 void MpiFileBuffer::store_to_channel( Size slave, Size channel, std::string const& line ) {
350  //tr.Debug << "store channel for slave " << slave << " channel: " << channel << " length: " << line.length() << std::endl;
351  SingleFileBufferOP buf = open_buffers_[ channel ];
352  runtime_assert( buf ); //writing to open file ?
353  buf->store_line( slave, channel, line );
354  if (buf->length(slave) > 5e6) {
355  tr.Info << "autoflush threshold (5 MB) triggered for slave " << slave << " channel: " << channel << std::endl;
356  flush_channel(slave, channel);
357  }
358 }
359 
360 void MpiFileBuffer::flush_channel( Size slave, Size channel_id ) {
361  tr.Debug << "flush channel for slave " << slave << " channel: " << channel_id << std::endl;
362  SingleFileBufferOP buf = open_buffers_[ channel_id ];
363  runtime_assert( buf ); //writing to open file ?
364  buf->flush( slave );
365 }
366 
367 void MpiFileBuffer::close_channel( Size slave, Size channel_id ) {
368  SingleFileBufferOP buf = open_buffers_[ channel_id ];
369  runtime_assert( buf ); //writing to open file ?
370  buf->close( slave );
371  tr.Debug << "close channel "<< channel_id <<" for slave " << slave
372  << " currently " << buf->nr_open_slaves() << " open slave buffers; open files: " << open_buffers_.size() << std::endl;
373  if ( !buf->has_open_slaves() && bSlaveCanOpenFile_ && !bKeepFilesAlive_ ) {
374  tr.Debug << "channel has no more open slaves... close completely now" << std::endl;
375  close_file( channel_id );
376  mem_tr << "closed_channel" << std::endl;
377  } else {
378  if ( !buf->has_open_slaves() ) {
379  tr.Debug << "mark channel " << channel_id << " for closing in " << seconds_to_keep_files_alive_ << std::endl;
380  garbage_collector_[ channel_id ]=time(NULL);
381  }
382  }
383  tr.Debug << "currently " << open_buffers_.size() << " open buffers" << std::endl;
384 }
385 
387  if ( my_rank_ != buffer_rank_ ) {
388  tr.Debug << "remote close file " << filename << " on node " << my_rank_ << std::endl;
389  return remote_close_file( filename );
390  } else {
391  if ( is_open_channel( filename ) ) {
392  Size channel_id = open_files_[ filename ];
393  tr.Debug << "close file " << filename << " with channel_id " << channel_id << std::endl;
394  close_file( channel_id );
395  return true;
396  }
397  }
398  return false;
399 }
400 
401 void MpiFileBuffer::close_file( Size channel_id ) {
402  Buffers::iterator iter = open_buffers_.find( channel_id );
403  if ( iter != open_buffers_.end() ) {
404  std::string filename( iter->second->filename() );
405  Filenames::iterator file_iter = open_files_.find( filename );
406  runtime_assert( file_iter != open_files_.end() ); //if this is not true we have inconsistency between open_buffers and open_files_
407  open_files_.erase( file_iter );
408  open_buffers_.erase( iter );
409  } else {
410  utility_exit_with_message( "illegal attempt to delete file with non-existant channel_id " + channel_id );
411  }
412 }
413 
414 SingleFileBufferOP WriteOut_MpiFileBuffer::generate_new_channel( std::string const& filename, Size channel, bool append, Size& status ) {
415  return new WriteFileSFB( filename, channel, append, status );
416 }
417 
418 SingleFileBufferOP DebugOut_MpiFileBuffer::generate_new_channel( std::string const& filename, Size channel, bool /*append*/, Size& status ) {
419  return new SingleFileBuffer( filename, channel, status );
420 }
421 
422 }
423 }