aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/msg_instrs.tab
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2017-11-10 05:09:46 +0100
committerBjörn Gustavsson <[email protected]>2017-11-14 06:35:56 +0100
commit3679444fb654e9cba1252c6df0be5170e5388639 (patch)
treec5357a66497a6af3208289b72d1ba907b395c98b /erts/emulator/beam/msg_instrs.tab
parent19245b1ac7bf881319950adb973ff2ce24fea23e (diff)
downloadotp-3679444fb654e9cba1252c6df0be5170e5388639.tar.gz
otp-3679444fb654e9cba1252c6df0be5170e5388639.tar.bz2
otp-3679444fb654e9cba1252c6df0be5170e5388639.zip
Fix broken receive optimization
When a ref is created before performing a receive that will only receive message containing that ref, there is a compiler optimization to avoid scanning messages that can't possible contain the newly created ref. Magnus Lång pointed out that the implementation of the optimization is flawed. Exceptions or recursive calls could cause the receive operation to scan the receive queue from a position beyond the expected message (that is, the message containing the ref would never be matched out). See the receive_opt_exception/1 and receive_opt_recursion/1 test cases in receive_SUITE. It turns out that we can simplify the implementation of the optimization while fixing the bug (suggested by Magnus Lång). We actually don't need the c_p->msg.mark field. It is enough to have c_p->msg.saved_pos; if it is non-zero, it is a valid position in the message qeueue. All we need to do is to ensure that we clear c_p->msg.saved_pos when a receive is exited normally or abnormally. We can clear c_p->msg.saved_pos in JOIN_MESSAGE(), since it is called both when leaving a receive because a message matched and because there was a timeout and the 'after' clause was executed. In addition, we need to clear c_p->msg.saved_pos when an exception is caught. https://bugs.erlang.org/browse/ERL-511
Diffstat (limited to 'erts/emulator/beam/msg_instrs.tab')
-rw-r--r--erts/emulator/beam/msg_instrs.tab21
1 files changed, 9 insertions, 12 deletions
diff --git a/erts/emulator/beam/msg_instrs.tab b/erts/emulator/beam/msg_instrs.tab
index 8055a8616f..d6d4d2fb49 100644
--- a/erts/emulator/beam/msg_instrs.tab
+++ b/erts/emulator/beam/msg_instrs.tab
@@ -43,27 +43,23 @@
// *
// */
-recv_mark(Dest) {
+i_recv_mark() {
/*
- * Save the current position in message buffer and the
- * the label for the loop_rec/2 instruction for the
- * the receive statement.
+ * Save the current position in message buffer.
*/
- $SET_REL_I(c_p->msg.mark, $Dest);
c_p->msg.saved_last = c_p->msg.last;
}
i_recv_set() {
/*
- * If the mark is valid (points to the loop_rec/2
- * instruction that follows), we know that the saved
- * position points to the first message that could
- * possibly be matched out.
+ * If c_p->msg.saved_last is non-zero, it points to the first
+ * message that could possibly be matched out.
*
- * If the mark is invalid, we do nothing, meaning that
- * we will look through all messages in the message queue.
+ * If c_p->msg.saved_last is zero, it means that it was invalidated
+ * because another receive was executed before this i_recv_set()
+ * instruction was reached.
*/
- if (c_p->msg.mark == (BeamInstr *) ($NEXT_INSTRUCTION)) {
+ if (c_p->msg.saved_last) {
c_p->msg.save = c_p->msg.saved_last;
}
SET_I($NEXT_INSTRUCTION);
@@ -131,6 +127,7 @@ i_loop_rec(Dest) {
ASSERT(HTOP == c_p->htop && E == c_p->stop);
/* TODO: Add DTrace probe for this bad message situation? */
UNLINK_MESSAGE(c_p, msgp);
+ c_p->msg.saved_last = 0; /* Better safe than sorry. */
msgp->next = NULL;
erts_cleanup_messages(msgp);
goto loop_rec__;