diff --git a/qse/cmd/awk/Makefile.am b/qse/cmd/awk/Makefile.am index 7883f98b..3bd8ddfb 100644 --- a/qse/cmd/awk/Makefile.am +++ b/qse/cmd/awk/Makefile.am @@ -3,7 +3,8 @@ AUTOMAKE_OPTIONS = nostdinc AM_CPPFLAGS = \ -I$(top_builddir)/include \ -I$(top_srcdir)/include \ - -I$(includedir) + -I$(includedir) \ + -DDEFAULT_MODDIR=\"$(libdir)/qse\" #####################################################################3 @@ -18,4 +19,3 @@ if WCHAR qseawk_LDADD += $(UNICOWS_LIBS) endif endif - diff --git a/qse/cmd/awk/Makefile.in b/qse/cmd/awk/Makefile.in index fd2fa026..fc195425 100644 --- a/qse/cmd/awk/Makefile.in +++ b/qse/cmd/awk/Makefile.in @@ -254,7 +254,8 @@ AUTOMAKE_OPTIONS = nostdinc AM_CPPFLAGS = \ -I$(top_builddir)/include \ -I$(top_srcdir)/include \ - -I$(includedir) + -I$(includedir) \ + -DDEFAULT_MODDIR=\"$(libdir)/qse\" qseawk_SOURCES = awk.c qseawk_LDFLAGS = -L../../lib/awk -L../../lib/cmn -L$(libdir) diff --git a/qse/cmd/awk/awk.c b/qse/cmd/awk/awk.c index 40d9895f..3cb9d161 100644 --- a/qse/cmd/awk/awk.c +++ b/qse/cmd/awk/awk.c @@ -966,7 +966,7 @@ static int awk_main (int argc, qse_char_t* argv[]) qse_awk_parsestd_t psout; qse_mmgr_t* mmgr = QSE_MMGR_GETDFL(); - memset (&arg, 0, QSE_SIZEOF(arg)); + qse_memset (&arg, 0, QSE_SIZEOF(arg)); arg.icf.mmgr = mmgr; i = comparg (argc, argv, &arg); @@ -1129,9 +1129,88 @@ oops: return ret; } +struct mpi_t +{ + void* h; + int (*i) (int argc, qse_achar_t* argv[]); + void (*f) (void); +}; + +typedef struct mpi_t mpi_t; + +static void open_mpi (mpi_t* mpi, int argc, qse_achar_t* argv[]) +{ + lt_dladvise adv; + + qse_memset (mpi, 0, QSE_SIZEOF(*mpi)); + +#if defined(USE_LTDL) + + #if defined(QSE_ACHAR_IS_MCHAR) + if (qse_mbscmp (qse_mbsbasename(argv[0]), QSE_MT("qseawkmp")) != 0 && + qse_mbscmp (qse_mbsbasename(argv[0]), QSE_MT("qseawkmpi")) != 0) return; + #else + if (qse_wcscmp (qse_wcsbasename(argv[0]), QSE_WT("qseawkmp")) != 0 && + qse_wcscmp (qse_wcsbasename(argv[0]), QSE_WT("qseawkmpi")) != 0) return; + #endif + + if (lt_dlinit () != 0) return; + + if (lt_dladvise_init (&adv) != 0) goto oops; + + /* If i don't set the global option, loading may end up with an error + * like this depending on your MPI library. + * + * symbol lookup error: /usr/lib/openmpi/lib/openmpi/mca_paffinity_linux.so: undefined symbol: mca_base_param_reg_int + */ + + if (lt_dladvise_global (&adv) != 0 || lt_dladvise_ext (&adv) != 0) + { + lt_dladvise_destroy (&adv); + goto oops; + } + + mpi->h = lt_dlopenadvise (DEFAULT_MODDIR "/libawkmpi", adv); + lt_dladvise_destroy (&adv); + + if (mpi->h) + { + mpi->i = lt_dlsym (mpi->h, "mpi_init"); + mpi->f = lt_dlsym (mpi->h, "mpi_fini"); + + if (mpi->i == QSE_NULL || + mpi->f == QSE_NULL || + mpi->i (argc, argv) <= -1) + { + lt_dlclose (mpi->h); + mpi->h = QSE_NULL; + } + } + + return; + +oops: + lt_dlexit (); +#endif +} + +static void close_mpi (mpi_t* mpi) +{ + if (mpi->h) + { +#if defined(USE_LTDL) + mpi->f (); + lt_dlclose (mpi->h); + mpi->h = QSE_NULL; + lt_dlexit (); +#endif + } +} + int qse_main (int argc, qse_achar_t* argv[]) { int ret; + mpi_t mpi; #if defined(_WIN32) char locale[100]; @@ -1162,15 +1241,11 @@ int qse_main (int argc, qse_achar_t* argv[]) qse_setdflcmgrbyid (QSE_CMGR_SLMB); #endif -#if defined(USE_LTDL) - lt_dlinit (); -#endif - + open_mpi (&mpi, argc, argv); + ret = qse_runmain (argc, argv, awk_main); -#if defined(USE_LTDL) - lt_dlexit (); -#endif + close_mpi (&mpi); #if defined(_WIN32) WSACleanup (); diff --git a/qse/include/qse/awk/awk.h b/qse/include/qse/awk/awk.h index 216e338a..25251d05 100644 --- a/qse/include/qse/awk/awk.h +++ b/qse/include/qse/awk/awk.h @@ -1045,7 +1045,7 @@ enum qse_awk_trait_t QSE_AWK_MODERN = QSE_AWK_CLASSIC | QSE_AWK_EXTRAKWS | QSE_AWK_MAPTOVAR | - QSE_AWK_RWPIPE | QSE_AWK_REXBOUND | QSE_AWK_TOLERANT + QSE_AWK_RWPIPE | QSE_AWK_TOLERANT }; /* ------------------------------------------------------------------------ */ diff --git a/qse/lib/awk/StdAwk.cpp b/qse/lib/awk/StdAwk.cpp index 98eeaec2..be37a357 100644 --- a/qse/lib/awk/StdAwk.cpp +++ b/qse/lib/awk/StdAwk.cpp @@ -45,6 +45,7 @@ #else # include # include +# define USE_LTDL #endif #ifndef QSE_HAVE_CONFIG_H @@ -134,8 +135,7 @@ int StdAwk::open () this->gbl_environ <= -1 || this->gbl_procinfo <= -1) { - Awk::close (); - return -1; + goto oops; } if (addFunction (QSE_T("rand"), 0, 0, (FunctionHandler)&StdAwk::rand, 0) <= -1 || @@ -145,10 +145,17 @@ int StdAwk::open () addFunction (QSE_T("setioattr"), 3, 3, (FunctionHandler)&StdAwk::setioattr, QSE_AWK_RIO) <= -1 || addFunction (QSE_T("getioattr"), 2, 2, (FunctionHandler)&StdAwk::getioattr, QSE_AWK_RIO) <= -1) { - Awk::close (); - return -1; + goto oops; } +#if defined(USE_LTDL) + /* lt_dlinit() can be called more than once and + * lt_dlexit() shuts down libltdl if it's called as many times as + * corresponding lt_dlinit(). so it's safe to call lt_dlinit() + * and lt_dlexit() at the library level. */ + if (lt_dlinit() != 0) goto oops; +#endif + qse_ntime_t now; this->seed = (qse_gettime(&now) <= -1)? 0u: (long_t)now; @@ -160,6 +167,10 @@ int StdAwk::open () this->cmgrtab_inited = false; return 0; + +oops: + Awk::close (); + return -1; } void StdAwk::close () @@ -171,8 +182,13 @@ void StdAwk::close () this->cmgrtab_inited = false; } #endif + clearConsoleOutputs (); Awk::close (); + +#if defined(USE_LTDL) + lt_dlexit (); +#endif } StdAwk::Run* StdAwk::parse (Source& in, Source& out) diff --git a/qse/lib/awk/run.c b/qse/lib/awk/run.c index 918fc88d..35be21b7 100644 --- a/qse/lib/awk/run.c +++ b/qse/lib/awk/run.c @@ -827,14 +827,14 @@ void qse_awk_rtx_close (qse_awk_rtx_t* rtx) qse_awk_rtx_ecb_t* ecb; struct module_fini_ctx_t mfc; - for (ecb = rtx->ecb; ecb; ecb = ecb->next) - if (ecb->close) ecb->close (rtx); - mfc.limit = 0; mfc.count = 0; mfc.rtx = rtx; qse_rbt_walk (rtx->awk->modtab, fini_module, &mfc); + for (ecb = rtx->ecb; ecb; ecb = ecb->next) + if (ecb->close) ecb->close (rtx); + /* NOTE: * the close callbacks are called before data in rtx * is destroyed. if the destruction count on any data diff --git a/qse/lib/awk/std.c b/qse/lib/awk/std.c index aee949fc..c2a9127d 100644 --- a/qse/lib/awk/std.c +++ b/qse/lib/awk/std.c @@ -49,6 +49,7 @@ #else # include # include +# define USE_LTDL #endif #ifndef QSE_HAVE_CONFIG_H @@ -421,6 +422,9 @@ qse_awk_t* qse_awk_openstd (qse_size_t xtnsize) static void fini_xtn (qse_awk_t* awk) { /* nothing to do */ +#if defined(USE_LTDL) + lt_dlexit (); +#endif } static void clear_xtn (qse_awk_t* awk) @@ -447,6 +451,7 @@ qse_awk_t* qse_awk_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize) prm.math.log10 = custom_awk_log10; prm.math.exp = custom_awk_exp; prm.math.sqrt = custom_awk_sqrt; + prm.modopen = custom_awk_modopen; prm.modclose = custom_awk_modclose; prm.modsym = custom_awk_modsym; @@ -460,17 +465,25 @@ qse_awk_t* qse_awk_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize) /* add intrinsic global variables and functions */ if (add_globals(awk) <= -1 || - add_functions (awk) <= -1) - { - qse_awk_close (awk); - return QSE_NULL; - } + add_functions (awk) <= -1) goto oops; + +#if defined(USE_LTDL) + /* lt_dlinit() can be called more than once and + * lt_dlexit() shuts down libltdl if it's called as many times as + * corresponding lt_dlinit(). so it's safe to call lt_dlinit() + * and lt_dlexit() at the library level. */ + if (lt_dlinit () != 0) goto oops; +#endif xtn->ecb.close = fini_xtn; xtn->ecb.clear = clear_xtn; qse_awk_pushecb (awk, &xtn->ecb); return awk; + +oops: + if (awk) qse_awk_close (awk); + return QSE_NULL; } void* qse_awk_getxtnstd (qse_awk_t* awk) diff --git a/qse/mod/awk/mpi.c b/qse/mod/awk/mpi.c index 6f92afe6..fbf54e5b 100644 --- a/qse/mod/awk/mpi.c +++ b/qse/mod/awk/mpi.c @@ -1,7 +1,38 @@ -#include +#include #include +#include -#include +#if defined(HAVE_MPI) +# include +#else +# error this module needs mpi +#endif + +static int fnc_size (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) +{ + qse_awk_val_t* retv; + int rank; + + MPI_Comm_size (MPI_COMM_WORLD, &rank); + retv = qse_awk_rtx_makeintval (rtx, rank); + if (retv == QSE_NULL) return -1; + + qse_awk_rtx_setretval (rtx, retv); + return 0; +} + +static int fnc_rank (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) +{ + qse_awk_val_t* retv; + int rank; + + MPI_Comm_rank (MPI_COMM_WORLD, &rank); + retv = qse_awk_rtx_makeintval (rtx, rank); + if (retv == QSE_NULL) return -1; + + qse_awk_rtx_setretval (rtx, retv); + return 0; +} static int fnc_hash (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) { @@ -27,10 +58,13 @@ static int fnc_assign (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) if (rx >= 0) { qse_awk_nrflt_t nrflt; + int size, rank; + MPI_Comm_size (MPI_COMM_WORLD, &size); + MPI_Comm_rank (MPI_COMM_WORLD, &rank); nrflt.limit = limit; -// nrflt.size = rxtn->size; -// nrflt.rank = rxtn->rank; + nrflt.size = size; + nrflt.rank = rank; qse_awk_rtx_setnrflt (rtx, &nrflt); } @@ -41,15 +75,12 @@ static int fnc_assign (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) return 0; } -#if 0 static int fnc_reduce (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) { - qse_size_t nargs; - qse_awk_val_t* tmp, * a0, * a1; + qse_awk_val_t* retv; qse_long_t opidx, lv; qse_flt_t rv; int n; - rxtn_t* rxtn; static MPI_Op optab[] = { @@ -61,55 +92,40 @@ static int fnc_reduce (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) MPI_LOR }; - rxtn = (rxtn_t*) qse_awk_rtx_getxtnstd (rtx); - - nargs = qse_awk_rtx_getnargs (rtx); - QSE_ASSERT (nargs == 2); - - a0 = qse_awk_rtx_getarg (rtx, 0); - a1 = qse_awk_rtx_getarg (rtx, 1); - - if (qse_awk_rtx_valtolong (rtx, a1, &opidx) <= -1) return -1; - if (opidx < 0 || opidx >= QSE_COUNTOF(optab)) goto softfail; - if ((n = qse_awk_rtx_valtonum (rtx, a0, &lv, &rv)) <= -1) return -1; + if (qse_awk_rtx_valtolong (rtx, qse_awk_rtx_getarg (rtx, 1), &opidx) <= -1 || + (opidx < 0 || opidx >= QSE_COUNTOF(optab)) || + (n = qse_awk_rtx_valtonum (rtx, qse_awk_rtx_getarg (rtx, 0), &lv, &rv)) <= -1) goto softfail; /* TODO: determine it to be MPI_LONG or MPI_INT, OR MPI_LONG_LONG_INT depending on the size of qse_long_t */ -/* TODO: how to tell normal -1 from the soft failure??? */ if (n == 0) { qse_long_t lout; - if (MPI_Allreduce (&lv, &lout, 1, MPI_LONG_LONG_INT, optab[opidx], rxtn->comm) != MPI_SUCCESS) goto softfail; - tmp = qse_awk_rtx_makeintval (rtx, lout); + if (MPI_Allreduce (&lv, &lout, 1, MPI_LONG_LONG_INT, optab[opidx], MPI_COMM_WORLD) != MPI_SUCCESS) goto softfail; + retv = qse_awk_rtx_makeintval (rtx, lout); } else { qse_flt_t fout; - if (MPI_Allreduce (&rv, &fout, 1, MPI_LONG_DOUBLE, optab[opidx], rxtn->comm) != MPI_SUCCESS) goto softfail; - tmp = qse_awk_rtx_makefltval (rtx, fout); + if (MPI_Allreduce (&rv, &fout, 1, MPI_LONG_DOUBLE, optab[opidx], MPI_COMM_WORLD) != MPI_SUCCESS) goto softfail; + retv = qse_awk_rtx_makefltval (rtx, fout); } - if (tmp == QSE_NULL) return -1; - qse_awk_rtx_setretval (rtx, tmp); + if (retv == QSE_NULL) return -1; + qse_awk_rtx_setretval (rtx, retv); return 0; softfail: - tmp = qse_awk_rtx_makeintval (rtx, (qse_long_t)-1); - if (tmp == QSE_NULL) return -1; - qse_awk_rtx_setretval (rtx, tmp); + /* return without setting the return value. + * this intrinsic function will return a nil value when it fails */ return 0; } -#endif static int fnc_barrier (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) { int rx; qse_awk_val_t* retv; -// rxtn_t* rxtn; -// rxtn = (rxtn_t*) qse_awk_rtx_getxtnstd (rtx); - -// x = (MPI_Barrier (rxtn->comm) == MPI_SUCCESS)? 0: -1; rx = (MPI_Barrier (MPI_COMM_WORLD) == MPI_SUCCESS)? 0: -1; retv = qse_awk_rtx_makeintval (rtx, rx); @@ -119,43 +135,6 @@ static int fnc_barrier (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) return 0; } -static int fnc_init (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) -{ - int rx; - qse_awk_val_t* retv; - - /* I didn't manage to find a good way to change the - * default error handler to MPI_ERRORS_RETURN. - * so MPI_Init() will simply abort the program if it fails */ -// if (MPI_Init (&argc, &argv) != MPI_SUCCESS) rx = -1; -// else -// { - MPI_Comm_set_errhandler (MPI_COMM_WORLD, MPI_ERRORS_RETURN); - rx = 0; -// } - - retv = qse_awk_rtx_makeintval (rtx, rx); - if (retv == QSE_NULL) return -1; - - qse_awk_rtx_setretval (rtx, retv); - return 0; -} - -static int fnc_fini (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi) -{ - int rx; - qse_awk_val_t* retv; - - MPI_Finalize (); - rx = 0; - - retv = qse_awk_rtx_makeintval (rtx, rx); - if (retv == QSE_NULL) return -1; - - qse_awk_rtx_setretval (rtx, retv); - return 0; -} - /* ------------------------------------------------------------------------- */ typedef struct fnctab_t fnctab_t; @@ -176,9 +155,20 @@ static fnctab_t fnctab[] = { { QSE_T("assign"), { { 1, 1 }, fnc_assign } }, { QSE_T("barrier"), { { 0, 0 }, fnc_barrier } }, - { QSE_T("fini"), { { 0, 0 }, fnc_fini } }, { QSE_T("hash"), { { 1, 1 }, fnc_hash } }, - { QSE_T("init"), { { 0, 0 }, fnc_init } } + { QSE_T("rank"), { { 0, 0 }, fnc_rank } }, + { QSE_T("reduce"), { { 2, 2 }, fnc_reduce } }, + { QSE_T("size"), { { 0, 0 }, fnc_size } } +}; + +static inttab_t inttab[] = +{ + { QSE_T("REDUCE_LAND"), { 4 } }, + { QSE_T("REDUCE_LOR"), { 5 } }, + { QSE_T("REDUCE_MAX"), { 1 } }, + { QSE_T("REDUCE_MIN"), { 0 } }, + { QSE_T("REDUCE_PROD"), { 3 } }, + { QSE_T("REDUCE_SUM"), { 2 } } }; static int query (qse_awk_mod_t* mod, qse_awk_t* awk, const qse_char_t* name, qse_awk_mod_sym_t* sym) @@ -197,7 +187,6 @@ static int query (qse_awk_mod_t* mod, qse_awk_t* awk, const qse_char_t* name, qs } } -#if 0 for (i = 0; i < QSE_COUNTOF(inttab); i++) { if (qse_strcmp (inttab[i].name, name) == 0) @@ -207,7 +196,6 @@ static int query (qse_awk_mod_t* mod, qse_awk_t* awk, const qse_char_t* name, qs return 0; } } -#endif ea.ptr = name; ea.len = qse_strlen(name); @@ -217,12 +205,13 @@ static int query (qse_awk_mod_t* mod, qse_awk_t* awk, const qse_char_t* name, qs /* TODO: proper resource management */ -int init (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx) +static int init (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx) { + /* TODO: anything */ return 0; } -void fini (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx) +static void fini (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx) { /* TODO: anything */ } @@ -246,3 +235,30 @@ int load (qse_awk_mod_t* mod, qse_awk_t* awk) return 0; } +/* The MPI module is special in that it exports 2 extra symbols - + * mpi_init and mpi_fini. These two symbols are intended to be called + * dynamically using dlopen() or something similar when an application + * intending to use mpi::xxx starts up. This way, the application doesn't + * have to be linked to any MPI libraries while this module is linked to + * an MPI library. If this module doesn't exist, it means MPI is not availble + * and the module wasn't built. So you can't access mpi::xxx symbols either + */ + +int mpi_init (int argc, qse_achar_t* argv[]) +{ + int rx; + + if (MPI_Init (&argc, &argv) != MPI_SUCCESS) rx = -1; + else + { + MPI_Comm_set_errhandler (MPI_COMM_WORLD, MPI_ERRORS_RETURN); + rx = 0; + } + + return rx; +} + +void mpi_fini (void) +{ + MPI_Finalize (); +} diff --git a/qse/mod/awk/sys.c b/qse/mod/awk/sys.c index 485a9811..f7039000 100644 --- a/qse/mod/awk/sys.c +++ b/qse/mod/awk/sys.c @@ -1,4 +1,4 @@ -#include +#include #include #if defined(_WIN32) @@ -298,12 +298,12 @@ static int query (qse_awk_mod_t* mod, qse_awk_t* awk, const qse_char_t* name, qs /* TODO: proper resource management */ -int init (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx) +static int init (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx) { return 0; } -void fini (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx) +static void fini (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx) { /* TODO: for (each pid for rtx) kill (pid, SIGKILL); diff --git a/qse/mod/awk/uci.c b/qse/mod/awk/uci.c index 5ac30039..8ea8251a 100644 --- a/qse/mod/awk/uci.c +++ b/qse/mod/awk/uci.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/qse/samples/awk/awk08.cpp b/qse/samples/awk/awk08.cpp index c0ca4c13..48ed6318 100644 --- a/qse/samples/awk/awk08.cpp +++ b/qse/samples/awk/awk08.cpp @@ -36,8 +36,6 @@ # include # include # include -# include -# define USE_LTDL #endif /* these three definitions for doxygen cross-reference */ @@ -58,6 +56,8 @@ public: idLastSleep = addGlobal (QSE_T("LAST_SLEEP")); if (idLastSleep <= -1) goto oops; + /* this is for demonstration only. + * you can use sys::sleep() instead */ if (addFunction (QSE_T("sleep"), 1, 1, (FunctionHandler)&MyAwk::sleep) <= -1) goto oops; @@ -470,16 +470,8 @@ int qse_main (int argc, qse_achar_t* argv[]) qse_setdflcmgrbyid (QSE_CMGR_SLMB); #endif -#if defined(USE_LTDL) - lt_dlinit (); -#endif - ret = qse_runmain (argc, argv, awk_main); -#if defined(USE_LTDL) - lt_dlexit (); -#endif - #if defined(_WIN32) WSACleanup (); #endif