Changeset: f2457c0b28b5 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/f2457c0b28b5
Modified Files:
.bumpversion.cfg
MonetDB.spec
clients/mapilib/mapi.rc
clients/odbc/driver/driver.rc
clients/odbc/winsetup/setup.rc
cmake/monetdb-versions.cmake
gdk/gdk_bat.c
gdk/libbat.rc
monetdb5/tools/libmonetdb5.rc
Branch: default
Log Message:
Merge with Sep2022.
diffs (truncated from 425 to 300 lines):
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -785,3 +785,4 @@ 47675351fec22d5d5dc81eec03dfa08f505afc99
47675351fec22d5d5dc81eec03dfa08f505afc99 Sep2022_release
44e45c9a451f6afd933773094ec25723f713d3be Jan2022_23
44e45c9a451f6afd933773094ec25723f713d3be Jan2022_SP5_release
+43d4a717410d6f6692a16a878640fc7e0f248725 Jan2022_25
diff --git a/clients/examples/C/testcondvar.c b/clients/examples/C/testcondvar.c
--- a/clients/examples/C/testcondvar.c
+++ b/clients/examples/C/testcondvar.c
@@ -16,7 +16,12 @@
volatile int timeout = 100; // set this to 0 during interactive debugging
-/* global state protected by a lock: */
+/* global state protected by a lock.
+ *
+ * Every worker thread has a number of permits and a number of ticks.
+ * It sleeps on the condition variable, whenever it wakes it will
+ * try to convert one permit into a tick, that is, permit-- and tick++.
+ */
MT_Lock lock = MT_LOCK_INITIALIZER(lock);
MT_Cond condvar = MT_COND_INITIALIZER(the_condvar);
@@ -28,7 +33,15 @@ struct state {
bool terminated;
} states[NN] = { {0} };
-
+/*
+ * The main thread holds the lock so it can manipulate and verify the permits
+ * and ticks. It uses this function to temporarily release the lock so the
+ * workers get a chance to do their work. We give them a 100ms, which should be
+ * plenty.
+ *
+ * If we cannot retake the lock after that interval we assume a worker thread
+ * has gone astray while holding the lock.
+ */
static void
let_run(void)
{
@@ -36,16 +49,17 @@ let_run(void)
MT_sleep_ms(100);
+ // try to retake the lock. Make a few attempts before giving up.
int attempts = 0;
while (!MT_lock_try(&lock)) {
if (timeout > 0 && ++attempts > timeout) {
fprintf(stderr, "Can't get hold of the lock after %d
attempts\n", attempts);
+ fprintf(stderr, "If this is because you're running this
program in a debugger,\n");
+ fprintf(stderr, "try setting the timeout variable to
0.\n");
abort();
}
MT_sleep_ms(10);
}
-
- fprintf(stderr, "\n");
}
@@ -54,9 +68,10 @@ worker(void *arg)
{
struct state *st = arg;
int id = (int)(st - &states[0]);
- fprintf(stderr, "worker %d starting\n", id);
+ fprintf(stderr, "worker %d started, waiting to acquire lock\n", id);
MT_lock_set(&lock);
+ fprintf(stderr, "worker %d acquired lock\n", id);
while (1) {
if (st->terminate) {
fprintf(stderr, "worker %d terminating\n", id);
@@ -67,7 +82,7 @@ worker(void *arg)
st->ticks++;
st->permits--;
}
- fprintf(stderr, "worker %d waiting\n", id);
+ fprintf(stderr, "worker %d waiting on condvar\n", id);
MT_cond_wait(&condvar, &lock);
fprintf(stderr, "worker %d woke up\n", id);
}
@@ -76,16 +91,6 @@ worker(void *arg)
}
-static void clear(void)
-{
- for (int i = 0; i < NN; i++) {
- struct state *st = &states[i];
- st->permits = 0;
- st->ticks = 0;
- }
-}
-
-
static void
check_impl(int line, int expected_sum_ticks, int expected_max_ticks, int
expected_sum_permits)
{
@@ -101,17 +106,26 @@ check_impl(int line, int expected_sum_ti
max_ticks = ticks;
}
+ fprintf(stderr, "On line %d: (sum_ticks, max_ticks, sum_permits) = (%d,
%d, %d)\n",
+ line,
+ sum_ticks, max_ticks, sum_permits);
+
bool good = true;
good &= (sum_ticks == expected_sum_ticks);
- good &= (max_ticks == expected_max_ticks);
+ if (expected_max_ticks >= 0)
+ good &= (max_ticks == expected_max_ticks);
good &= (sum_permits == expected_sum_permits);
if (good)
return;
- fprintf(stderr, "\nOn line %d:\n", line);
- fprintf(stderr, "Expect sum ticks to be %d, is %d\n",
expected_sum_ticks, sum_ticks);
- fprintf(stderr, "Expect max ticks to be %d, is %d\n",
expected_max_ticks, max_ticks);
- fprintf(stderr, "Expect sum permits to be %d, is %d\n",
expected_sum_permits, sum_permits);
+ if (expected_max_ticks >= 0) {
+ fprintf(stderr, "MISMATCH: expected (%d, %d, %d)\n",
+ expected_sum_ticks, expected_max_ticks,
expected_sum_permits);
+ } else {
+ fprintf(stderr, "MISMATCH: expected (%d, ?, %d)\n",
+ expected_sum_ticks, expected_sum_permits);
+ }
+
for (int i = 0; i < NN; i++) {
fprintf(stderr, "worker %d: ticks=%d permits=%d\n", i,
states[i].ticks, states[i].permits);
}
@@ -125,46 +139,54 @@ main(void)
{
MT_thread_init();
+ // All code in this function runs while we hold the lock.
+ // From time to time we call let_run() to allow the worker threads to
obtain it.
MT_lock_set(&lock);
+
+ fprintf(stderr, "-- Initially, everything is zero\n");
check(0, 0, 0);
+ fprintf(stderr, "\n-- Starting the worker threads\n");
for (int i = 0; i < NN; i++) {
struct state *st = &states[i];
char name[MT_NAME_LEN];
snprintf(name, sizeof(name), "worker%d", i);
MT_create_thread(&st->id, worker, st, MT_THR_JOINABLE, name);
}
- check(0, 0, 0);
+ MT_sleep_ms(100);
+ fprintf(stderr, "\n-- Now allow the workers to take the lock, they
should enter their main loop\n");
let_run();
check(0, 0, 0);
- // give them all a permit and broadcast on the condvar. they should all
run
+ fprintf(stderr, "\n-- All threads get a permit but nothing happens
because we haven't touched the condvar\n");
for (int i = 0; i < NN; i++)
states[i].permits = 1;
let_run();
- // haven't notified them yet:
- check(0, 0, 3);
+ check(0, 0, NN);
+
+ fprintf(stderr, "\n-- Now we broadcast on the condvar. All should
wake\n");
MT_cond_broadcast(&condvar);
let_run();
- check(3, 1, 0);
+ check(NN, 1, 0);
+
+ fprintf(stderr, "\n-- Now we give each of them %d permits\n", NN);
+ for (int i = 0; i < NN; i++) {
+ states[i].ticks = 0;
+ states[i].permits = NN;
+ }
+ check(0, 0, NN * NN);
- // when using signal, we need to trigger them three times
- clear();
- for (int i = 0; i < NN; i++)
- states[i].permits = 1;
- let_run();
- check(0, 0, 3);
- MT_cond_signal(&condvar);
- let_run();
- check(1, 1, 2);
- MT_cond_signal(&condvar);
- let_run();
- check(2, 1, 1);
- MT_cond_signal(&condvar);
- let_run();
- check(3, 1, 0);
+ // Note: counting from 1 instead of 0
+ for (int i = 1; i <= NN; i++) {
+ fprintf(stderr, "\n-- [%d] Signal one, don't know which one it
will be\n", i);
+ MT_cond_signal(&condvar);
+ let_run();
+ check(i, -1, NN * NN - i);
+ }
+
+ fprintf(stderr, "\n-- Telling them all to quit\n");
for (int i = 0; i < NN; i++) {
states[i].terminate = true;
}
@@ -172,12 +194,11 @@ main(void)
let_run();
for (int i = 0; i < NN; i++) {
- fprintf(stderr, "joining worker %d\n", i);
+ fprintf(stderr, "-- Joining worker %d\n", i);
MT_join_thread(states[i].id);
+ fprintf(stderr, "-- Joined worker %d\n", i);
}
- fprintf(stderr, "joined all, exiting\n");
+ fprintf(stderr, "\n-- Joined all, exiting\n");
- (void)worker;
- (void)let_run;
return 0;
}
diff --git a/gdk/gdk_bat.c b/gdk/gdk_bat.c
--- a/gdk/gdk_bat.c
+++ b/gdk/gdk_bat.c
@@ -2198,9 +2198,14 @@ backup_new(Heap *hp, bool lock)
char *batpath, *bakpath;
struct stat st;
+ char *bak_filename = NULL;
+ if ((bak_filename = strrchr(hp->filename, DIR_SEP)) != NULL)
+ bak_filename++;
+ else
+ bak_filename = hp->filename;
/* check for an existing X.new in BATDIR, BAKDIR and SUBDIR */
batpath = GDKfilepath(hp->farmid, BATDIR, hp->filename, "new");
- bakpath = GDKfilepath(hp->farmid, BAKDIR, hp->filename, "new");
+ bakpath = GDKfilepath(hp->farmid, BAKDIR, bak_filename, "new");
if (batpath != NULL && bakpath != NULL) {
/* file actions here interact with the global commits */
if (lock)
diff --git a/sql/test/concurrent/Tests/All b/sql/test/concurrent/Tests/All
--- a/sql/test/concurrent/Tests/All
+++ b/sql/test/concurrent/Tests/All
@@ -4,3 +4,4 @@ segments-corruption
read-segment-after-free
smart-segment-merge
many-concurrent-client-connections
+truncate-insert-flood
diff --git a/sql/test/concurrent/Tests/truncate-insert-flood.py
b/sql/test/concurrent/Tests/truncate-insert-flood.py
new file mode 100644
--- /dev/null
+++ b/sql/test/concurrent/Tests/truncate-insert-flood.py
@@ -0,0 +1,66 @@
+import sys, os, tempfile, pymonetdb
+
+try:
+ from MonetDBtesting import process
+except ImportError:
+ import process
+
+from concurrent.futures import ThreadPoolExecutor
+
+nr_clients = 16
+
+with tempfile.TemporaryDirectory() as dbfarm:
+ os.mkdir(os.path.join(dbfarm, 'db'))
+
+ with open(os.path.join(dbfarm, 'errout'), mode='a+') as errout:
+ with process.server(
+ dbname='db',
+ dbfarm=os.path.join(dbfarm, 'db'),
+ args=[ "--set", "gdk_nr_threads=0", "--forcemito"],
+ stdin=process.PIPE,
+ stdout=process.PIPE,
+ stderr=errout,
+ mapiport='0') as db_proc:
+
+ def client(id):
+ conn = pymonetdb.connect(
+ database='db',
+ port=db_proc.dbport,
+ autocommit=True)
+ cursor = conn.cursor()
+
+ init = f'''
+ drop table if exists foo_{id};
+ create table foo_{id} (c1, c2, c3, c4, c5) AS VALUES
+ (10, 20, 30, 40, 50),
+ (11, 21, 31, 41, 51),
+ (12, 22, 32, 42, 52);
+ set optimizer = 'minimal_fast';
+ '''
+ cursor.execute(init)
+
+ truncate_and_insert_queries = f"""
+ truncate foo_{id};
+ insert into foo_{id} VALUES
+ (10, 20, 30, 40, 50),
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]