--- deliantra/server/lib/cf.pm 2006/12/30 16:56:16 1.104 +++ deliantra/server/lib/cf.pm 2006/12/31 18:10:40 1.107 @@ -15,8 +15,10 @@ use Coro::Timer; use Coro::Signal; use Coro::Semaphore; +use Coro::AIO; -use IO::AIO 2.3; +use Fcntl; +use IO::AIO 2.31 (); use YAML::Syck (); use Time::HiRes; @@ -174,6 +176,47 @@ JSON::Syck::Dump $_[0] } +=item cf::sync_job { BLOCK } + +The design of crossfire+ requires that the main coro ($Coro::main) is +always able to handle events or runnable, as crossfire+ is only partly +reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable. + +If it must be done, put the blocking parts into C. This will run +the given BLOCK in another coroutine while waiting for the result. The +server will be frozen during this time, so the block should either finish +fast or be very important. + +=cut + +sub sync_job(&) { + my ($job) = @_; + + my $busy = 1; + my @res; + + # TODO: use suspend/resume instead + local $FREEZE = 1; + + my $coro = Coro::async { + @res = eval { $job->() }; + warn $@ if $@; + undef $busy; + }; + + if ($Coro::current == $Coro::main) { + $coro->prio (Coro::PRIO_MAX); + while ($busy) { + Coro::cede_notself; + Event::one_event unless Coro::nready; + } + } else { + $coro->join; + } + + wantarray ? @res : $res[0] +} + =item $coro = cf::coro { BLOCK } Creates and returns a new coro. This coro is automcatially being canceled @@ -565,31 +608,38 @@ sub object_freezer_save { my ($filename, $rdata, $objs) = @_; - if (length $$rdata) { - warn sprintf "saving %s (%d,%d)\n", - $filename, length $$rdata, scalar @$objs; - - if (open my $fh, ">:raw", "$filename~") { - chmod SAVE_MODE, $fh; - syswrite $fh, $$rdata; - close $fh; + sync_job { + if (length $$rdata) { + warn sprintf "saving %s (%d,%d)\n", + $filename, length $$rdata, scalar @$objs; - if (@$objs && open my $fh, ">:raw", "$filename.pst~") { + if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) { chmod SAVE_MODE, $fh; - syswrite $fh, Storable::nfreeze { version => 1, objs => $objs }; + aio_write $fh, 0, (length $$rdata), $$rdata, 0; + aio_fsync $fh; close $fh; - rename "$filename.pst~", "$filename.pst"; + + if (@$objs) { + if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) { + chmod SAVE_MODE, $fh; + my $data = Storable::nfreeze { version => 1, objs => $objs }; + aio_write $fh, 0, (length $data), $data, 0; + aio_fsync $fh; + close $fh; + aio_rename "$filename.pst~", "$filename.pst"; + } + } else { + aio_unlink "$filename.pst"; + } + + aio_rename "$filename~", $filename; } else { - unlink "$filename.pst"; + warn "FATAL: $filename~: $!\n"; } - - rename "$filename~", $filename; } else { - warn "FATAL: $filename~: $!\n"; + aio_unlink $filename; + aio_unlink "$filename.pst"; } - } else { - unlink $filename; - unlink "$filename.pst"; } } @@ -604,20 +654,18 @@ sub object_thawer_load { my ($filename) = @_; - local $/; + my ($data, $av); - my $av; + (aio_load $filename, $data) >= 0 + or return; - #TODO: use sysread etc. - if (open my $data, "<:raw:perlio", $filename) { - $data = <$data>; - if (open my $pst, "<:raw:perlio", "$filename.pst") { - $av = eval { (Storable::thaw <$pst>)->{objs} }; - } - return ($data, $av); + unless (aio_stat "$filename.pst") { + (aio_load "$filename.pst", $av) >= 0 + or return; + $av = eval { (Storable::thaw <$av>)->{objs} }; } - () + return ($data, $av); } ############################################################################# @@ -1235,13 +1283,20 @@ ############################################################################# # initialisation -sub _perl_reload() { +sub perl_reload() { + # can/must only be called in main + if ($Coro::current != $Coro::main) { + warn "can only reload from main coroutine\n"; + return; + } + warn "reloading..."; - eval { - local $FREEZE = 1; + local $FREEZE = 1; + cf::emergency_save; - cf::emergency_save; + eval { + # if anything goes wrong in here, we should simply crash as we already saved # cancel all watchers for (Event::all_watchers) { @@ -1310,14 +1365,15 @@ warn "reattach"; _global_reattach; }; - warn $@ if $@; - warn "reloaded"; -}; + if ($@) { + warn $@; + warn "error while reloading, exiting."; + exit 1; + } -sub perl_reload() { - _perl_reload; -} + warn "reloaded successfully"; +}; register "", __PACKAGE__; @@ -1325,8 +1381,9 @@ my ($who, $arg) = @_; if ($who->flag (FLAG_WIZ)) { - $who->message ("reloading..."); - _perl_reload; + $who->message ("start of reload."); + perl_reload; + $who->message ("end of reload."); } };