Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Proof-of-concept handling of SIGINT via fsl_cx_interrupt() by way of fcli. Currently only honored by fsl_repo_rebuild() but "should" eventually be honored by any unusually-long-running operations. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
2a405470c0d32a84f8644de1b5c50963 |
User & Date: | stephan 2021-11-18 17:40:56.695 |
Context
2021-11-18
| ||
18:03 | Tighten up the interruption flag handling and fcli's inclusion (or not) of sigaction(). check-in: 2c0147b596 user: stephan tags: trunk | |
17:40 | Proof-of-concept handling of SIGINT via fsl_cx_interrupt() by way of fcli. Currently only honored by fsl_repo_rebuild() but "should" eventually be honored by any unusually-long-running operations. check-in: 2a405470c0 user: stephan tags: trunk | |
16:31 | A workaround for an as-yet-unexplained case of an attempt to rebuild the same artifict twice. Shows up on my pi4 but not my x86 system. check-in: 242c38c7b2 user: stephan tags: trunk | |
Changes
Changes to auto.def.
︙ | ︙ | |||
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | \#if !defined(HAVE_STDINT_H) \# define HAVE_STDINT_H 1 \#endif \#endif /* _WIN32 */ $FSL_PLATFORM_CONFIG_H \#endif /* $incGuard */ " close $out puts "Generated $ofile." return } # end of --amal bootstrap config generation wh-require-bash cc-check-c11 cc-check-sizeof "void *" if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib compress z]} { user-error "Missing functional zlib" } cc-check-function-in-lib iconv iconv #if {![cc-check-functions iconv] && # ![cc-check-function-in-lib iconv iconv]} { # user-error "Cannot find iconv(3) in libc or libiconv" #} ######################################################################## | > > > > > > > > > > > > | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | \#if !defined(HAVE_STDINT_H) \# define HAVE_STDINT_H 1 \#endif \#endif /* _WIN32 */ $FSL_PLATFORM_CONFIG_H \#if !defined(HAVE_SIGACTION) \# if defined(FSL_AMALGAMATION_BUILD) \# if defined(FSL_PLATFORM_IS_UNIX) \# define HAVE_SIGACTION 1 \# else \# define HAVE_SIGACTION 0 \# endif \# else \# define HAVE_SIGACTION 0 \# endif \#endif \#endif /* $incGuard */ " close $out puts "Generated $ofile." return } # end of --amal bootstrap config generation wh-require-bash cc-check-c11 cc-check-sizeof "void *" if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib compress z]} { user-error "Missing functional zlib" } cc-check-function-in-lib iconv iconv cc-check-functions sigaction fork #if {![cc-check-functions iconv] && # ![cc-check-function-in-lib iconv iconv]} { # user-error "Cannot find iconv(3) in libc or libiconv" #} ######################################################################## |
︙ | ︙ | |||
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 | \#if !defined(HAVE_STAT) \# define HAVE_STAT 0 \#endif \#endif /*_WIN32*/ $FSL_PLATFORM_CONFIG_H \#endif /* $incGuard */ " close $out puts "Generated $confH." if {[opt-bool dump-defines]} { msg-result "--dump-defines is creating file: $DUMP_DEFINES_FILE" make-config-header $DUMP_DEFINES_FILE -auto {*} } | > > > | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | \#if !defined(HAVE_STAT) \# define HAVE_STAT 0 \#endif \#endif /*_WIN32*/ $FSL_PLATFORM_CONFIG_H \#if !defined(HAVE_SIGACTION) \# define HAVE_SIGACTION [get-define HAVE_SIGACTION 0] \#endif \#endif /* $incGuard */ " close $out puts "Generated $confH." if {[opt-bool dump-defines]} { msg-result "--dump-defines is creating file: $DUMP_DEFINES_FILE" make-config-header $DUMP_DEFINES_FILE -auto {*} } |
Changes to f-apps/f-rebuild.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include "fossil-scm/fossil-internal.h" // Only for testing/debugging.. #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) /*** For reference (noting that there are no plans to to implement all of these) options... $ fossil help rebuild | > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include "fossil-scm/fossil-internal.h" // Only for testing/debugging.. #define MARKER(pfexp) \ do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ printf pfexp; \ } while(0) /*** For reference (noting that there are no plans to to implement all of these) options... $ fossil help rebuild |
︙ | ︙ | |||
305 306 307 308 309 310 311 | if(!frs->qDeltas.stmt){ rc = fsl_cx_prepare(frs->f, &frs->qDeltas, "SELECT rid FROM delta WHERE srcid=?1/*%s()*/", __func__); }else{ fsl_stmt_reset(&frs->qDeltas); } | < | 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | if(!frs->qDeltas.stmt){ rc = fsl_cx_prepare(frs->f, &frs->qDeltas, "SELECT rid FROM delta WHERE srcid=?1/*%s()*/", __func__); }else{ fsl_stmt_reset(&frs->qDeltas); } while(0==rc && rid>0){ //MARKER(("TODO: %s(rid=%d)\n", __func__, (int)rid)); if(blobSize != (int64_t)content->used){ /* Fix [blob.size] field if needed. (Why would this ever be needed?) */ rc = fsl_stmt_bind_step(&frs->qSize, "IR", (int64_t)content->used, rid); if(rc){ |
︙ | ︙ | |||
331 332 333 334 335 336 337 338 339 340 341 342 343 344 | while(0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&frs->qDeltas)){ fsl_id_t const cid = fsl_stmt_g_id(&frs->qDeltas, 0); if(!fsl_id_bag_contains(&frs->idsDone, cid)){ rc = fsl_id_bag_insert(&idsChildren, cid); } } fsl_stmt_reset(&frs->qDeltas); if(rc) break; fsl_size_t const nChild = fsl_id_bag_count(&idsChildren); if(fsl_id_bag_contains(&frs->idsDone, rid)){ /* Kludge! This check should not be necessary. Testing with the libfossil repo, this check is not required on the main x86 dev machine but is on a Raspberry Pi 4, for reasons | > | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | while(0==rc && FSL_RC_STEP_ROW==fsl_stmt_step(&frs->qDeltas)){ fsl_id_t const cid = fsl_stmt_g_id(&frs->qDeltas, 0); if(!fsl_id_bag_contains(&frs->idsDone, cid)){ rc = fsl_id_bag_insert(&idsChildren, cid); } } fsl_stmt_reset(&frs->qDeltas); rc = INTCHECK rc; if(rc) break; fsl_size_t const nChild = fsl_id_bag_count(&idsChildren); if(fsl_id_bag_contains(&frs->idsDone, rid)){ /* Kludge! This check should not be necessary. Testing with the libfossil repo, this check is not required on the main x86 dev machine but is on a Raspberry Pi 4, for reasons |
︙ | ︙ | |||
364 365 366 367 368 369 370 | deconstruct, performing different work for each. We're skipping the deconstruct option for now but may want to add it later. See fossil's rebuild.c:rebuild_step(). Note that deconstruct is not a capability intended for normal client use. It's primarily for testing of fossil itself. */ fsl_deck_init(frs->f, &deck, FSL_SATYPE_ANY); //MARKER(("rid=%d\n", (int)rid)); | | | 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | deconstruct, performing different work for each. We're skipping the deconstruct option for now but may want to add it later. See fossil's rebuild.c:rebuild_step(). Note that deconstruct is not a capability intended for normal client use. It's primarily for testing of fossil itself. */ fsl_deck_init(frs->f, &deck, FSL_SATYPE_ANY); //MARKER(("rid=%d\n", (int)rid)); rc = INTCHECK fsl_deck_parse2(&deck, &deckContent, rid) /* But isn't it okay if rid is not an artifact? */; switch(rc){ case FSL_RC_SYNTAX: /* assume deck is not an artifact */ fsl_cx_err_reset(frs->f); rc = 0; /* Note that fossil ignores this case and continues processing the |
︙ | ︙ | |||
389 390 391 392 393 394 395 | if(0==rc){ rid = 0; continue; } #endif break; case 0: | | > | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | if(0==rc){ rid = 0; continue; } #endif break; case 0: rc = INTCHECK fsl__deck_crosslink(&deck); break; default: #if 0 MARKER(("err=%s for rid=%d content=\n%.*s\n", fsl_rc_cstr(rc), (int)rid, (int)deckContent.used, (char const *)deckContent.mem)); #endif break; } fsl_buffer_clear(&deckContent); rc = INTCHECK 0; if(0==rc && 0==(rc = frs->f->interrupted)){ rc = fsl__rebuild_step_done( frs, rid ); } if(rc) break; /* Process all dependent deltas recursively... */ doChildren: rid = 0; |
︙ | ︙ | |||
450 451 452 453 454 455 456 | rc = fsl_cx_err_set(frs->f, rc, "Error applying delta #%" FSL_ID_T_PFMT " to parent #%" FSL_ID_T_PFMT, cid, rid); } goto outro; } if(i<nChild){ | | > | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | rc = fsl_cx_err_set(frs->f, rc, "Error applying delta #%" FSL_ID_T_PFMT " to parent #%" FSL_ID_T_PFMT, cid, rid); } goto outro; } if(i<nChild){ rc = INTCHECK fsl__rebuild_step(frs, cid, sz, &next); assert(!next.mem); rc = INTCHECK rc; }else{ /* Tail recursion */ rid = cid; blobSize = sz; fsl_buffer_clear(content); *content = next/*transfer ownership*/; } |
︙ | ︙ | |||
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 | result codes from the db layer). It runs in a transaction and will roll back on non-0, so it "shouldn't" leave a mess on error. If opt->dryRun is true then it also rolls back the transaction but it is not treated as an error. During the rebuild process opt->callback will, if it is not NULL, be called to provide feedback on its progress. */ int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt); int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt){ int rc = 0; fsl_db * const db = fsl_needs_repo(f); if(!db) return rc; rc = fsl_cx_transaction_begin(f); if(0==rc){ rc = fsl__rebuild(f, opt); int const rc2 = fsl_cx_transaction_end(f, opt->dryRun || rc!=0); if(0==rc && 0!=rc2) rc = rc2; } return rc; } | > > > > > > > < | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 | result codes from the db layer). It runs in a transaction and will roll back on non-0, so it "shouldn't" leave a mess on error. If opt->dryRun is true then it also rolls back the transaction but it is not treated as an error. During the rebuild process opt->callback will, if it is not NULL, be called to provide feedback on its progress. This operation honors interruption via fsl_cx_interrupt() but, due to intricacies of timing, it's possible that a triggered interrupt gets trumped by another error which happens while the check for the interrupt flag is pending. In both cases this function could return a non-0 code, though. */ int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt); int fsl_repo_rebuild(fsl_cx * const f, fsl_rebuild_opt const * const opt){ int rc = 0; fsl_db * const db = fsl_needs_repo(f); if(!db) return rc; rc = fsl_cx_transaction_begin(f); if(0==rc){ rc = fsl__rebuild(f, opt); int const rc2 = fsl_cx_transaction_end(f, opt->dryRun || rc!=0); if(0==rc && 0!=rc2) rc = rc2; } fsl_cx_interrupt(f, 0, NULL); return rc; } static int fsl_rebuild_f_my(fsl_rebuild_step const * const state){ if(state->stepNumber){ f_out("\r%" PRIu32 " of %" PRIu32, state->stepNumber, state->artifactCount); }else{ f_out("\n"); |
︙ | ︙ | |||
787 788 789 790 791 792 793 | if(!quiet){ ropt.callback = fsl_rebuild_f_my; ropt.callbackState = NULL; } fsl_timer_start(&timer); rc = fsl_repo_rebuild(fcli_cx(), &ropt); { | | > | 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 | if(!quiet){ ropt.callback = fsl_rebuild_f_my; ropt.callbackState = NULL; } fsl_timer_start(&timer); rc = fsl_repo_rebuild(fcli_cx(), &ropt); { f_out("%sWork took a total of %f ms of CPU time.\n", ropt.callback ? "\n" : "", (double)(fsl_timer_reset(&timer) / 1000.0)); fcli_dump_cache_metrics(); } f_out("\n"); end: return fcli_end_of_main(rc); } |
Changes to include/fossil-scm/fossil-core.h.
︙ | ︙ | |||
2090 2091 2092 2093 2094 2095 2096 | @see fsl_confirm_callback_f @see fsl_cx_confirmer() */ FSL_EXPORT int fsl_cx_confirm(fsl_cx * const f, fsl_confirm_detail const * detail, fsl_confirm_response *outAnswer); /** | | > | | | | > > > | > > > > > | > > > > > > | 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 | @see fsl_confirm_callback_f @see fsl_cx_confirmer() */ FSL_EXPORT int fsl_cx_confirm(fsl_cx * const f, fsl_confirm_detail const * detail, fsl_confirm_response *outAnswer); /** Sets f's is-interrupted flag and its error state. The is-interrupted flag is separate from f's normal error state and is _not_ cleared by fsl_cx_err_reset(). To clear the interrupted flag, call this function with values of 0 and NULL for the 2nd and 3rd arguments, respective. This flag _is_ fetched by fsl_cx_err_get() but it is possible for any error message provided via this routine to be overwritten or reset by another routine before the interrupted flag can be acted upon, whereas the interrupted flag itself can only be modified by this routine. Returns its 2nd argument on success or FSL_RC_OOM if given a formatted string and allocation of it fails. If passed a code of 0, the is-interrupted flag is reset but the general error state is not. Results are undefined if this function is called twice concurrently. i.e. all calls must come from a single thread. Results are also undefined if it is called while f is in its finalization phase. ACHTUNG: this is new as of 2021-11-18 and is not yet widely honored within the API. @see fsl_cx_interrupted() @see fsl_cx_interruptv() */ FSL_EXPORT int fsl_cx_interrupt(fsl_cx * const f, int code, const char * fmt, ...); /** The va_list counterpart of fsl_cx_interrupt(). */ FSL_EXPORT int fsl_cx_interruptv(fsl_cx * const f, int code, char const * fmt, va_list args); /** If f's is-interrupted flag is set, this function returns its value. Note that there is inherently a race condition when calling fsl_cx_interrupt() (to set the flag) from another thread (e.g. a UI thread while showing a progress indicator). */ |
︙ | ︙ |
Changes to src/cli.c.
︙ | ︙ | |||
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 | checking in an allocates-often API. */ static const fsl_allocator fcli_allocator = { fsl_realloc_f_failing, NULL/*state*/ }; void fcli_pre_setup(void){ static int run = 0; if(run++) return; fslAllocOrig = fsl_lib_configurable.allocator; fsl_lib_configurable.allocator = fcli_allocator /* This MUST be done BEFORE the fsl API allocates ANY memory! */; atexit(fcli_shutdown); } /** oldMode must be true if fcli.cliFlags is NULL, else false. */ static int fcli_setup_common1(bool oldMode, int argc, char const * const *argv){ static char once = 0; int rc = 0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 | checking in an allocates-often API. */ static const fsl_allocator fcli_allocator = { fsl_realloc_f_failing, NULL/*state*/ }; #define FCLI_USE_SIGNALS HAVE_SIGACTION #if FCLI_USE_SIGNALS #include <signal.h> /* sigaction(), if our feature macros are set right */ /** SIGINT handler which calls fsl_cx_interrupt(). */ static void fcli__sigc_handler(int s){ static fsl_cx * f = 0; if(f) return/*disable concurrent interruption*/; f = fcli_cx(); if(f){ f_out("^C\n"); fsl_cx_interrupt(f, FSL_RC_INTERRUPTED, "Interrupted by signal #%d.", s); f = NULL; } } #endif /* ^^^ FCLI_USE_SIGNALS */ void fcli_pre_setup(void){ static int run = 0; if(run++) return; fslAllocOrig = fsl_lib_configurable.allocator; fsl_lib_configurable.allocator = fcli_allocator /* This MUST be done BEFORE the fsl API allocates ANY memory! */; atexit(fcli_shutdown); #if FCLI_USE_SIGNALS struct sigaction sigIntHandler; sigIntHandler.sa_handler = fcli__sigc_handler; sigemptyset(&sigIntHandler.sa_mask); sigIntHandler.sa_flags = 0; sigaction(SIGINT, &sigIntHandler, NULL); #endif } /** oldMode must be true if fcli.cliFlags is NULL, else false. */ static int fcli_setup_common1(bool oldMode, int argc, char const * const *argv){ static char once = 0; int rc = 0; |
︙ | ︙ | |||
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 | return fcli_setup_v2(argc, argv, NULL, NULL); } int fcli_err_report2(bool clear, char const * file, int line){ int errRc = 0; char const * msg = NULL; errRc = fsl_error_get( fcli__error, &msg, NULL ); if(FCLI_RC_HELP==errRc){ errRc = 0; }else if(errRc || msg){ if(fcli.clientFlags.verbose>0){ fcli_printf("%s %s:%d: ERROR #%d (%s): %s\n", fcli.appName, file, line, errRc, fsl_rc_cstr(errRc), msg); }else{ fcli_printf("%s: ERROR #%d (%s): %s\n", fcli.appName, errRc, fsl_rc_cstr(errRc), msg); } } | > > > > > | > > | 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 | return fcli_setup_v2(argc, argv, NULL, NULL); } int fcli_err_report2(bool clear, char const * file, int line){ int errRc = 0; char const * msg = NULL; errRc = fsl_error_get( fcli__error, &msg, NULL ); if(!errRc && fcli.f && fcli.f->interrupted){ errRc = fcli.f->interrupted; msg = "Interrupted."; } if(FCLI_RC_HELP==errRc){ errRc = 0; }else if(errRc || msg){ if(fcli.clientFlags.verbose>0){ fcli_printf("%s %s:%d: ERROR #%d (%s): %s\n", fcli.appName, file, line, errRc, fsl_rc_cstr(errRc), msg); }else{ fcli_printf("%s: ERROR #%d (%s): %s\n", fcli.appName, errRc, fsl_rc_cstr(errRc), msg); } } if(clear){ fcli_err_reset(); if(fcli.f) fsl_cx_interrupt(fcli.f, 0, NULL); } return errRc; } const char * fcli_next_arg(bool remove){ const char * rc = (fcli.argc>0) ? fcli.argv[0] : NULL; if(rc && remove){ |
︙ | ︙ | |||
1278 1279 1280 1281 1282 1283 1284 | void fcli_dump_cache_metrics(void){ fsl_cx * const f = fcli.f; if(!f) return; f_out("fsl_cx::cache::mcache hits = %u misses = %u\n", f->cache.mcache.hits, f->cache.mcache.misses); f_out("fsl_cx::cache::blobContent hits = %u misses = %u. " | | > | 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 | void fcli_dump_cache_metrics(void){ fsl_cx * const f = fcli.f; if(!f) return; f_out("fsl_cx::cache::mcache hits = %u misses = %u\n", f->cache.mcache.hits, f->cache.mcache.misses); f_out("fsl_cx::cache::blobContent hits = %u misses = %u. " "Entry count=%u totaling %u byte(s).\n", f->cache.blobContent.metrics.hits, f->cache.blobContent.metrics.misses, f->cache.blobContent.used, f->cache.blobContent.szTotal); } #undef FCLI_V3 #undef fcli_empty_m #undef fcli__error #undef MARKER #undef FCLI_USE_SIGNALS |
Changes to src/cx.c.
︙ | ︙ | |||
2028 2029 2030 2031 2032 2033 2034 | goto end; } bool fsl_cx_has_ckout(fsl_cx const * const f ){ return f->ckout.dir ? true : false; } | > > > > > > > > > > | > > > > > | | 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 | goto end; } bool fsl_cx_has_ckout(fsl_cx const * const f ){ return f->ckout.dir ? true : false; } int fsl_cx_interruptv(fsl_cx * const f, int code, char const * fmt, va_list args){ if(code){ f->interrupted = code; code = fsl_cx_err_setv(f, code, fmt, args); }else{ f->interrupted = 0; } return code; } int fsl_cx_interrupt(fsl_cx * const f, int code, char const * fmt, ...){ int rc; va_list args; va_start(args,fmt); rc = fsl_cx_interruptv(f, code, fmt, args); va_end(args); return rc; } int fsl_cx_interrupted(fsl_cx const * const f){ return f->interrupted; } #if 0 |
︙ | ︙ |