# # delete_file "unix/platform_netsync.cc" # # delete_file "win32/platform_netsync.cc" # # patch "ChangeLog" # from [9732c2da22342d073b8540ea3a7ff22c4eb43826] # to [84f3a7c5b46f2794f54efaad9f464c530670daeb] # # patch "Makefile.am" # from [7c4be2eab69309599304f8e10db73a1757c7c9b3] # to [c2264e8c492e621716d9426975a3c805a99873b2] # # patch "file_io.cc" # from [e39a1c13030c5378ea4cb3aa9621148ce0c8fb7d] # to [c5541a89410a0194523e41bfcadb27f58124ee70] # # patch "main.cc" # from [ca8383ced00bdb686905f58eaeac7b1446347219] # to [93c14a170425bd2be15e68d0f4eed476f9b77144] # # patch "monotone.cc" # from [cab8b2b01a23a5e82a37761b90517703c57c241a] # to [07d7a968c1ea02cff2f2cd60def4980c6e781c21] # # patch "netsync.cc" # from [eb8b25717752569affa73b4c1fcbc03e33a9f701] # to [ce8405b9bfd4cb50e56835abc83d23325c041014] # # patch "platform.hh" # from [0c8532e0798d470031bc897517234e3c8f4b9be9] # to [9ab0db8ce56f0fead355f2e8477669748bee8a7e] # # patch "std_hooks.lua" # from [a2a27daced7769d54850f059f39554cb1e0fc78d] # to [e3b18c1cbca5bc0e1b47151cb273f6c335c81abe] # # patch "ui.cc" # from [0fa61fa5c584e05a187d6c7e29c8336e4d7f3455] # to [3cbe9c661750040b4663d3f1b05d85ff964707ff] # # patch "unix/fs.cc" # from [1bb3dcff181451829c42a1d34c1111d486437fda] # to [6b71610c94a70ecabfecdcf10f0aa0d880fffc7f] # # patch "win32/fs.cc" # from [bfc8bc3aaa07a3ec97a5d555ee4af563a118546d] # to [409c24f9c6ca1d1b9de8f951885c3044d16dfbd0] # ======================================================================== --- ChangeLog 9732c2da22342d073b8540ea3a7ff22c4eb43826 +++ ChangeLog 84f3a7c5b46f2794f54efaad9f464c530670daeb @@ -1,3 +1,26 @@ +2005-10-06 Matthew Gregan + + * std_hooks.lua: Pass '--eval' rather than '-eval' to Emacs; the + CVS emacsclient does not consider '-eval' as valid. + * platform.hh, unix/fs.cc, win32/fs.cc (rename_clobberingly): New + function for best-effort atomic renames. This will probably never + be atomic on Win32. + * file_io.cc (write_data_impl): Append process ID to temporary + file name so that multiple monotone processes operating on the + same working copy are less likely to clobber each other. Use + rename_clobberingly(). + * ui.cc (user_interface): Allow std::cout to raise + ios_base::failure exceptions if stream goes bad (e.g. stream + receives EPIPE). + * monotone.cc (cpp_main): Catch ios_base::failure exceptions and + exit cleanly. + * main.cc (main_with_signal_handlers): Install handler to ignore + SIGPIPE. + * Makefile.am: Remove platform_netsync.cc references. + * {unix,win32}/platform_netsync.cc: Remove files. + * platform.hh, netsync.cc (run_netsync_protocol): + Remove {start,end}_platform_netsync() calls. + 2005-10-04 Matt Johnston * botan/, Makefile.am: update to Botan 1.4.7 ======================================================================== --- Makefile.am 7c4be2eab69309599304f8e10db73a1757c7c9b3 +++ Makefile.am c2264e8c492e621716d9426975a3c805a99873b2 @@ -201,11 +201,11 @@ UNIX_PLATFORM_SOURCES = \ unix/read_password.cc unix/get_system_flavour.cc unix/process.cc unix/terminal.cc \ - unix/platform_netsync.cc unix/inodeprint.cc unix/fs.cc + unix/inodeprint.cc unix/fs.cc WIN32_PLATFORM_SOURCES = \ win32/read_password.cc win32/get_system_flavour.cc win32/process.cc win32/terminal.cc \ - win32/platform_netsync.cc win32/inodeprint.cc win32/fs.cc + win32/inodeprint.cc win32/fs.cc # primaries ======================================================================== --- file_io.cc e39a1c13030c5378ea4cb3aa9621148ce0c8fb7d +++ file_io.cc c5541a89410a0194523e41bfcadb27f58124ee70 @@ -359,7 +359,8 @@ // nb: no mucking around with multiple-writer conditions. we're a // single-user single-threaded program. you get what you paid for. assert_path_is_directory(bookkeeping_root); - bookkeeping_path tmp = bookkeeping_root / "data.tmp"; + bookkeeping_path tmp = bookkeeping_root / (boost::format("data.tmp.%d") % + get_process_id()).str(); { // data.tmp opens @@ -371,9 +372,7 @@ // data.tmp closes } - if (path_exists(p)) - N(fs::remove(mkdir(p)), F("removing %s failed") % p); - fs::rename(mkdir(tmp), mkdir(p)); + rename_clobberingly(tmp, p); } void ======================================================================== --- main.cc ca8383ced00bdb686905f58eaeac7b1446347219 +++ main.cc 93c14a170425bd2be15e68d0f4eed476f9b77144 @@ -260,21 +260,28 @@ { typedef struct sigaction* sigaction_ptr; static struct sigaction all_signals_action; + static struct sigaction ignore_signals_action; struct sigaction old_SIGFPE_action; struct sigaction old_SIGTRAP_action; struct sigaction old_SIGSEGV_action; struct sigaction old_SIGBUS_action; struct sigaction old_SIGABRT_action; + struct sigaction old_SIGPIPE_action; all_signals_action.sa_flags = 0; all_signals_action.sa_handler = &unix_style_signal_handler; sigemptyset(&all_signals_action.sa_mask); + ignore_signals_action.sa_flags = 0; + ignore_signals_action.sa_handler = SIG_IGN; + sigemptyset(&ignore_signals_action.sa_mask); + sigaction(SIGFPE , &all_signals_action, &old_SIGFPE_action); sigaction(SIGTRAP, &all_signals_action, &old_SIGTRAP_action); sigaction(SIGSEGV, &all_signals_action, &old_SIGSEGV_action); sigaction(SIGBUS , &all_signals_action, &old_SIGBUS_action); sigaction(SIGABRT, &all_signals_action, &old_SIGABRT_action); + sigaction(SIGPIPE, &ignore_signals_action, &old_SIGPIPE_action); int result = 0; bool trapped_signal = false; @@ -315,6 +322,7 @@ sigaction(SIGSEGV, &old_SIGSEGV_action, sigaction_ptr()); sigaction(SIGBUS , &old_SIGBUS_action , sigaction_ptr()); sigaction(SIGABRT, &old_SIGABRT_action, sigaction_ptr()); + sigaction(SIGPIPE, &old_SIGPIPE_action, sigaction_ptr()); if(trapped_signal) throw unix_signal_exception(em); ======================================================================== --- monotone.cc cab8b2b01a23a5e82a37761b90517703c57c241a +++ monotone.cc 07d7a968c1ea02cff2f2cd60def4980c6e781c21 @@ -548,6 +548,11 @@ clean_shutdown = true; return 1; } + catch (std::ios_base::failure const & ex) + { + clean_shutdown = true; + return 1; + } clean_shutdown = true; return ret; ======================================================================== --- netsync.cc eb8b25717752569affa73b4c1fcbc03e33a9f701 +++ netsync.cc ce8405b9bfd4cb50e56835abc83d23325c041014 @@ -3829,7 +3829,6 @@ { try { - start_platform_netsync(); if (voice == server_voice) { serve_connections(role, include_pattern, exclude_pattern, app, @@ -3849,14 +3848,11 @@ } catch (Netxx::NetworkException & e) { - end_platform_netsync(); throw informative_failure((F("network error: %s") % e.what()).str()); } catch (Netxx::Exception & e) { - end_platform_netsync(); throw oops((F("network error: %s") % e.what()).str());; } - end_platform_netsync(); } ======================================================================== --- platform.hh 0c8532e0798d470031bc897517234e3c8f4b9be9 +++ platform.hh 9ab0db8ce56f0fead355f2e8477669748bee8a7e @@ -34,10 +34,6 @@ // return value of 0 means "unlimited" unsigned int terminal_width(); -// for netsync -void start_platform_netsync(); -void end_platform_netsync(); - // for "reckless mode" working copy change detection. // returns 'true' if it has generated a valid inodeprint; returns 'false' if // there was a problem, in which case we should act as if the inodeprint has @@ -59,5 +55,7 @@ typedef enum { nonexistent, directory, file } status; }; path::status get_path_status(any_path const & path); - + +void rename_clobberingly(any_path const & from, any_path const & to); + #endif // __PLATFORM_HH__ ======================================================================== --- std_hooks.lua a2a27daced7769d54850f059f39554cb1e0fc78d +++ std_hooks.lua e3b18c1cbca5bc0e1b47151cb273f6c335c81abe @@ -337,17 +337,17 @@ local elisp = "(ediff-merge-files \"%s\" \"%s\" nil \"%s\")" return function() - return execute(emacs, "-eval", + return execute(emacs, "--eval", string.format(elisp, lfile, rfile, outfile)) end end function merge3_emacs_cmd(emacs, lfile, afile, rfile, outfile) local elisp = "(ediff-merge-files-with-ancestor \"%s\" \"%s\" \"%s\" nil \"%s\")" - local cmd_fmt = "%s -eval " .. elisp + local cmd_fmt = "%s --eval " .. elisp return function() - execute(emacs, "-eval", + execute(emacs, "--eval", string.format(elisp, lfile, rfile, afile, outfile)) end end ======================================================================== --- ui.cc 0fa61fa5c584e05a187d6c7e29c8336e4d7f3455 +++ ui.cc 3cbe9c661750040b4663d3f1b05d85ff964707ff @@ -262,6 +262,7 @@ last_write_was_a_tick(false), t_writer(0) { + cout.exceptions(ios_base::badbit); #ifndef WIN32 clog.sync_with_stdio(false); #endif ======================================================================== --- unix/fs.cc 1bb3dcff181451829c42a1d34c1111d486437fda +++ unix/fs.cc 6b71610c94a70ecabfecdcf10f0aa0d880fffc7f @@ -104,3 +104,10 @@ E(false, F("cannot handle special file %s") % path); } } + +void +rename_clobberingly(any_path const & from, any_path const & to) +{ + E(!rename(from.as_external().c_str(), to.as_external().c_str()), + F("renaming '%s' to '%s' failed: %s") % from % to % std::strerror(errno)); +} ======================================================================== --- win32/fs.cc bfc8bc3aaa07a3ec97a5d555ee4af563a118546d +++ win32/fs.cc 409c24f9c6ca1d1b9de8f951885c3044d16dfbd0 @@ -21,7 +21,7 @@ F("cannot get working directory: %s") % strerror(errno)); return std::string(buffer); } - + void change_current_working_dir(any_path const & to) { E(!chdir(to.as_external().c_str()), @@ -114,3 +114,54 @@ else return path::file; } + +static bool +rename_clobberingly_impl(const char* from, const char* to) +{ + // MoveFileEx is only available on NT-based systems. We will revert to a + // more compatible DeleteFile/MoveFile pair as a compatibility fall-back. + typedef BOOL (*MoveFileExFun)(LPCTSTR, LPCTSTR, DWORD); + static MoveFileExFun MoveFileEx = 0; + if (MoveFileEx == 0) { + HMODULE hModule = LoadLibrary("kernel32"); + MoveFileEx = reinterpret_cast + (GetProcAddress(hModule, "MoveFileExA")); + if (MoveFileEx) + L(F("using MoveFileEx for renames")); + } + + if (MoveFileEx) { + if (MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING)) + return true; + } else { + // This is not even remotely atomic, but what can you do? + DeleteFile(to); + if (MoveFile(from, to)) + return true; + } + return false; +} + +void +rename_clobberingly(any_path const & from, any_path const & to) +{ + const char* szFrom = from.as_external().c_str(); + const char* szTo = to.as_external().c_str(); + static const int renameAttempts = 16; + DWORD sleepTime = 1; + + // If a clobbering rename attempt fails, we wait and try again, up to an + // (arbitrary) maximum of 16 attempts. This is a gross hack to work + // around the common problem where another process (e.g. a virus checker) + // will exclusive open a file you've just touched. + for (int i = 0; i < renameAttempts; ++i) { + if (rename_clobberingly_impl(szFrom, szTo)) + return; + L(F("attempted rename of '%s' to '%s' failed: %d") + % szFrom % szTo % GetLastError()); + Sleep(sleepTime); + if (sleepTime < 250) + sleepTime *= 2; + } + E(false, F("renaming '%s' to '%s' failed: %d") % from % to); +}