-
Notifications
You must be signed in to change notification settings - Fork 379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
All triggers, visible to a user, crash on non-trivial changes #4264
Comments
Affected places: space:on_replace, before_replace, box.on_commit, box.on_rollback, box.session.on_connect, and all the others using lbox_trigger_reset. They are public. |
* Vladislav Shpilevoy <notifications@github.com> [19/06/01 06:03]:
Affected places: space:on_replace, before_replace,
box.on_commit, box.on_rollback, box.session.on_connect, and all
the others using lbox_trigger_reset. They are public.
I believe this is documented even. I remember discussing the
problem with PeterG when triggers were introduced. IMO fixing this
is just not worth it. We can simply state the behaviour is
undefined. In Tarantool you can trivially crash the system
(ffi.cast and dereference a null pointer), so we are not obliged
to fix all of the crash scenarios just because it-is-a-crash.
…--
Konstantin Osipov, Moscow, Russia
|
It is a bug, reproduced without any C manipulations. If you consider it not important, then you can move it to wishlist. |
As I understand, Kirill considers it a bug, as I do. Here are my thoughts on how we could resolve it. It should contain a flag Problems with my approach are
This mindblowing way is really hard to implement and support. There is another one, but less flexible. Lets define struct rlist_mutable {
struct base;
int version;
}; Version is contained in the head only, incremented on each change. Before iteration the version is saved. If during iteration the version was changed, then abort the cycle. We will document that behaviour, that attempts to change a trigger list during iteration will stop its current processing. |
IMHO, we need to ban this. No segfaults are allowed. |
Kirill, this is contrary to triage guidelines. Please do not waste time on matters with low impact. |
Looks like documented undefined behavior: I don't think we need to fix this, because:
|
This is documented UB, closing. |
It's not necessary for a trigger to clear another trigger to crash. For example, a trigger may yield (this is allowed, I believe?) and while it yields some other code might clear the next-to-be-run trigger. This will result in a crash (actually, in an infinite loop). |
struct trigger is about to get a new field, and it's mandatory that this field is specified in all initializers. Let's better switch to explicit trigger_create() in all initialization code, to avoid specifying the new field everywhere. Part-of tarantool#4264 NO_DOC=refactoring NO_CHANGELOG=refactoring NO_TEST=refactoring
This patch fixes a number of issues with trigger_clear() while the trigger list is being run: 1) clearing the next-to-be-run trigger doesn't prevent it from being run 2) clearing the next-to-be-run trigger causes an infinite loop or a crash Closes tarantool#4264
Make trigger_fiber_run return an error, when it occurs, so that the calling code decides how to log it. Also, while I'm at it, simplify trigger_fiber_run's code a bit. In-scope-of tarantool#4264 NO_DOC=refactoring NO_TEST=refactoring NO_CHANGELOG=refactoring
struct trigger is about to get a new field, and it's mandatory that this field is specified in all initializers. Let's introduce a macro to avoid adding every new field to all the initializers and at the same time keep the benefits of static initialization. Also while we're at it fix `lbox_trigger_reset` setting all trigger fileds manually. Part-of tarantool#4264 NO_DOC=refactoring NO_CHANGELOG=refactoring NO_TEST=refactoring
This patch fixes a number of issues with trigger_clear() while the trigger list is being run: 1) clearing the next-to-be-run trigger doesn't prevent it from being run 2) clearing the next-to-be-run trigger causes an infinite loop or a crash 3) swapping trigger list head before the last trigger is run causes an infinite loop or a crash (see space_swap_triggers() in alter.cc, which had worked all this time by miracle: space _space on_replace trigger swaps its own head during local recovery, and that had only worked because the trigger by luck was the last to run) This is fixed by adding triggers in a separate run list on trigger_run. This list may be iterated by `rlist_shift_entry`, which doesn't suffer from any of the problems mentioned above. While being bad in a number of ways, old approach supported practically unlimited number of concurrent trigger_runs for the same trigger list. The new approach requires the trigger to be in as many run lists as there are concurrent trigger_runs, which results in quite a big refactoring. Add a luatest-based test and a unit test. Closes tarantool#4264 NO_DOC=bugfix
Our triggers support recursive invocation: for example, an on_replace trigger on a space may do a replace in the same space. However, this is not tested and might get broken easily. Let's add a corresponding test. In-scope-of tarantool#4264 NO_DOC=testing NO_CHANGELOG=testing
Unit test compilation with `#define UNIT_TAP_COMPATIBLE 1` might fail with an error complaining that <stdarg.h> is not included. Fix this. In-scope-of tarantool#4264 NO_CHANGELOG=testing stuff NO_DOC=testing stuff
cord_exit should be always called in the exiting thread. It's a single place to call all the thread-specific module deinitalization routines. In-scope-of tarantool#4264 NO_DOC=refactoring NO_TEST=refactoring NO_CHANGELOG=refactoring
Make trigger_fiber_run return an error, when it occurs, so that the calling code decides how to log it. Also, while I'm at it, simplify trigger_fiber_run's code a bit. In-scope-of tarantool#4264 NO_DOC=refactoring NO_TEST=refactoring NO_CHANGELOG=refactoring
struct trigger is about to get a new field, and it's mandatory that this field is specified in all initializers. Let's introduce a macro to avoid adding every new field to all the initializers and at the same time keep the benefits of static initialization. Also while we're at it fix `lbox_trigger_reset` setting all trigger fileds manually. Part-of tarantool#4264 NO_DOC=refactoring NO_CHANGELOG=refactoring NO_TEST=refactoring
This patch fixes a number of issues with trigger_clear() while the trigger list is being run: 1) clearing the next-to-be-run trigger doesn't prevent it from being run 2) clearing the next-to-be-run trigger causes an infinite loop or a crash 3) swapping trigger list head before the last trigger is run causes an infinite loop or a crash (see space_swap_triggers() in alter.cc, which had worked all this time by miracle: space _space on_replace trigger swaps its own head during local recovery, and that had only worked because the trigger by luck was the last to run) This is fixed by adding triggers in a separate run list on trigger_run. This list may be iterated by `rlist_shift_entry`, which doesn't suffer from any of the problems mentioned above. While being bad in a number of ways, old approach supported practically unlimited number of concurrent trigger_runs for the same trigger list. The new approach requires the trigger to be in as many run lists as there are concurrent trigger_runs, which results in quite a big refactoring. Add a luatest-based test and a unit test. Closes tarantool#4264 NO_DOC=bugfix
Our triggers support recursive invocation: for example, an on_replace trigger on a space may do a replace in the same space. However, this is not tested and might get broken easily. Let's add a corresponding test. In-scope-of #4264 NO_DOC=testing NO_CHANGELOG=testing
Unit test compilation with `#define UNIT_TAP_COMPATIBLE 1` might fail with an error complaining that <stdarg.h> is not included. Fix this. In-scope-of #4264 NO_CHANGELOG=testing stuff NO_DOC=testing stuff
cord_exit should be always called in the exiting thread. It's a single place to call all the thread-specific module deinitalization routines. In-scope-of #4264 NO_DOC=refactoring NO_TEST=refactoring NO_CHANGELOG=refactoring
Make trigger_fiber_run return an error, when it occurs, so that the calling code decides how to log it. Also, while I'm at it, simplify trigger_fiber_run's code a bit. In-scope-of #4264 NO_DOC=refactoring NO_TEST=refactoring NO_CHANGELOG=refactoring
struct trigger is about to get a new field, and it's mandatory that this field is specified in all initializers. Let's introduce a macro to avoid adding every new field to all the initializers and at the same time keep the benefits of static initialization. Also while we're at it fix `lbox_trigger_reset` setting all trigger fileds manually. Part-of #4264 NO_DOC=refactoring NO_CHANGELOG=refactoring NO_TEST=refactoring
This patch fixes a number of issues with trigger_clear() while the trigger list is being run: 1) clearing the next-to-be-run trigger doesn't prevent it from being run 2) clearing the next-to-be-run trigger causes an infinite loop or a crash 3) swapping trigger list head before the last trigger is run causes an infinite loop or a crash (see space_swap_triggers() in alter.cc, which had worked all this time by miracle: space _space on_replace trigger swaps its own head during local recovery, and that had only worked because the trigger by luck was the last to run) This is fixed by adding triggers in a separate run list on trigger_run. This list may be iterated by `rlist_shift_entry`, which doesn't suffer from any of the problems mentioned above. While being bad in a number of ways, old approach supported practically unlimited number of concurrent trigger_runs for the same trigger list. The new approach requires the trigger to be in as many run lists as there are concurrent trigger_runs, which results in quite a big refactoring. Add a luatest-based test and a unit test. Closes #4264 NO_DOC=bugfix
Our triggers support recursive invocation: for example, an on_replace trigger on a space may do a replace in the same space. However, this is not tested and might get broken easily. Let's add a corresponding test. In-scope-of #4264 NO_DOC=testing NO_CHANGELOG=testing (cherry picked from commit bf852b4)
struct trigger is about to get a new field, and it's mandatory that this field is specified in all initializers. Let's introduce a macro to avoid adding every new field to all the initializers and at the same time keep the benefits of static initialization. Also while we're at it fix `lbox_trigger_reset` setting all trigger fileds manually. Part-of #4264 NO_DOC=refactoring NO_CHANGELOG=refactoring NO_TEST=refactoring (cherry picked from commit 2040d1f)
This patch fixes a number of issues with trigger_clear() while the trigger list is being run: 1) clearing the next-to-be-run trigger doesn't prevent it from being run 2) clearing the next-to-be-run trigger causes an infinite loop or a crash 3) swapping trigger list head before the last trigger is run causes an infinite loop or a crash (see space_swap_triggers() in alter.cc, which had worked all this time by miracle: space _space on_replace trigger swaps its own head during local recovery, and that had only worked because the trigger by luck was the last to run) This is fixed by adding triggers in a separate run list on trigger_run. This list may be iterated by `rlist_shift_entry`, which doesn't suffer from any of the problems mentioned above. While being bad in a number of ways, old approach supported practically unlimited number of concurrent trigger_runs for the same trigger list. The new approach requires the trigger to be in as many run lists as there are concurrent trigger_runs, which results in quite a big refactoring. Add a luatest-based test and a unit test. Closes #4264 NO_DOC=bugfix (cherry picked from commit 607cb55)
Our triggers support recursive invocation: for example, an on_replace trigger on a space may do a replace in the same space. However, this is not tested and might get broken easily. Let's add a corresponding test. In-scope-of tarantool#4264 NO_DOC=testing NO_CHANGELOG=testing
Unit test compilation with `#define UNIT_TAP_COMPATIBLE 1` might fail with an error complaining that <stdarg.h> is not included. Fix this. In-scope-of tarantool#4264 NO_CHANGELOG=testing stuff NO_DOC=testing stuff
cord_exit should be always called in the exiting thread. It's a single place to call all the thread-specific module deinitalization routines. In-scope-of tarantool#4264 NO_DOC=refactoring NO_TEST=refactoring NO_CHANGELOG=refactoring
Make trigger_fiber_run return an error, when it occurs, so that the calling code decides how to log it. Also, while I'm at it, simplify trigger_fiber_run's code a bit. In-scope-of tarantool#4264 NO_DOC=refactoring NO_TEST=refactoring NO_CHANGELOG=refactoring
struct trigger is about to get a new field, and it's mandatory that this field is specified in all initializers. Let's introduce a macro to avoid adding every new field to all the initializers and at the same time keep the benefits of static initialization. Also while we're at it fix `lbox_trigger_reset` setting all trigger fileds manually. Part-of tarantool#4264 NO_DOC=refactoring NO_CHANGELOG=refactoring NO_TEST=refactoring
This patch fixes a number of issues with trigger_clear() while the trigger list is being run: 1) clearing the next-to-be-run trigger doesn't prevent it from being run 2) clearing the next-to-be-run trigger causes an infinite loop or a crash 3) swapping trigger list head before the last trigger is run causes an infinite loop or a crash (see space_swap_triggers() in alter.cc, which had worked all this time by miracle: space _space on_replace trigger swaps its own head during local recovery, and that had only worked because the trigger by luck was the last to run) This is fixed by adding triggers in a separate run list on trigger_run. This list may be iterated by `rlist_shift_entry`, which doesn't suffer from any of the problems mentioned above. While being bad in a number of ways, old approach supported practically unlimited number of concurrent trigger_runs for the same trigger list. The new approach requires the trigger to be in as many run lists as there are concurrent trigger_runs, which results in quite a big refactoring. Add a luatest-based test and a unit test. Closes tarantool#4264 NO_DOC=bugfix
This leads to surprising results. It could be a gamble to bet how that code will crash next time. Or will not crash. The only certain thing is that this code corrupts memory. While the example is not artificial. IMO a user should be able in a trigger to decide to drop all the triggers. Results are:
Or
Or
Or anything else. This is because all public triggers are run with function
trigger_run
, which usesrlist_foreach_safe
, but the latter can't withstand deletion of an element after current.Possible solution - introduce
struct rlist_safe
, or even do not use rlist for triggers at all. When a one wants to delete a trigger, it should be marked for deletion, but deleted only on a next attempt to run, for example.The text was updated successfully, but these errors were encountered: