Interesting. I also started experiencing this problem when I upgraded from 5.10 to 6.0. I upgraded the kernel because I also upgraded my hardware with a 13th gen CPU and monitor. I assumed the hardware upgrade introduced the problem but perhaps not.
If this is a kernel regression and you are serious about getting it fixed here is what you need to do.
Bisect the kernel to find the bad commit. Lookup guides on how to build the kernel for your distribution and guides for how to use git bisect. Bisecting the kernel is potentially dangerous because you can get a kernel with any broken commits.
Once you have the bad commit send an email to the author of that commit and CC the relevant mailing list. The list is probably intel-gfx@lists.freedesktop.org. You can use the script scripts/get_maintainer.pl
in the kernel source to find the relevant mailing list for any source file.
In the email describe the problem, the bad commit and state clearly that this is a userspace regression.
In my experience kernel developers rarely check bug trackers but they check their email.
This problem could be worked around in the kernel by introducing a switch to disable the hotplug notification feature. A smarter solution might use some heuristic to detect and ignore spurious hotplug events. You would have to convince Intel's devs that this is a problem that warrants attention.
Here is a patch I use for the v6.1 kernel to permanently disable all hotplug notifications. I never hotplug monitors when the computer is running so I don't care about those notifications anyway.
--- a/drivers/gpu/drm/i915/display/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/display/intel_hotplug.c
@@ -428,8 +428,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
drm_connector_list_iter_end(&conn_iter);
mutex_unlock(&dev->mode_config.mutex);
- if (changed)
- drm_kms_helper_hotplug_event(dev);
+ /* if (changed) don't send hotplug events to userspace
+ drm_kms_helper_hotplug_event(dev); */
/* Remove shared HPD pins that have changed */
retry &= ~changed;
Here are the relevant lines from drm.debug.0x1e-drm-tip:
[ 134.454841] i915 0000:00:02.0: [drm:gen8_de_irq_handler.isra.0 [i915]] hotplug event received, stat 0x02000000, dig 0x000008a0, pins 0x00000400, long 0x00000400
[ 134.460136] i915 0000:00:02.0: [drm:intel_encoder_hotplug [i915]] [CONNECTOR:249:HDMI-A-1] status updated from connected to disconnected (epoch counter 1->2)
[ 134.950057] i915 0000:00:02.0: [drm:gen8_de_irq_handler.isra.0 [i915]] hotplug event received, stat 0x02000000, dig 0x000008a0, pins 0x00000400, long 0x00000400
[ 135.950621] i915 0000:00:02.0: [drm:intel_encoder_hotplug [i915]] [CONNECTOR:249:HDMI-A-1] status updated from disconnected to connected (epoch counter 3->4)
[ 139.100342] i915 0000:00:02.0: [drm:gen8_de_irq_handler.isra.0 [i915]] hotplug event received, stat 0x02000000, dig 0x000008a0, pins 0x00000400, long 0x00000400
[ 139.102462] i915 0000:00:02.0: [drm:intel_encoder_hotplug [i915]] [CONNECTOR:249:HDMI-A-1] status updated from connected to disconnected (epoch counter 4->5)
[ 139.595501] i915 0000:00:02.0: [drm:gen8_de_irq_handler.isra.0 [i915]] hotplug event received, stat 0x02000000, dig 0x000008a0, pins 0x00000400, long 0x00000400
[ 140.589407] i915 0000:00:02.0: [drm:intel_encoder_hotplug [i915]] [CONNECTOR:249:HDMI-A-1] status updated from disconnected to connected (epoch counter 6->7)
I don't suppose you are hotplugging cables so this is what appears to be happening:
I have the same problem with a BenQ EX2710 monitor that gets activated by xfsettingsd (an xfce daemon). A lot of monitors will send hotplug events when they search for inputs. You might be able to configure your monitor to not automatically switch to active inputs to work around the problem.
This problem is the same as amd#662 but for the intel driver instead of amd. That bug has been open for 4 years with hundreds of "me too" comments so don't expect it to get fixed in the kernel.
tholin . (dc7cb454) at 03 Feb 17:04
For several years I've been having sporadic crashes of the X server. They always happens when I start to draw a mouse gesture using the easystroke-0.6.0 gesture program. The crashes are so rare they only happens 2-3 times per year making them difficult to debug.
I was finally able to catch a backtrace of a crash which I've attached to this report Xorg_sergfault.txt. The backtrace shows a null pointer deref at https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/mi/mipointer.c#L610
I'm no expert at C but one thing looks a bit suspicious to me. Thread 1 is waiting on the input_lock but immediately before that it modified the pPointer (https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/mi/mipointer.c#L276). That looks like the same pPointer causing the null pointer deref in Thread 6. Perhaps the locking in Thread 1 should also protect the pPointer?
running:
xorg-server 1.20.13 (can't see any relevant changes in 1.21)
xf86-input-evdev-2.10.6
xorg.conf:
Section "ServerFlags"
Option "LogVerbose" "10"
EndSection
Section "Device"
Identifier "IGP"
Driver "intel"
Option "TearFree" "true"
Option "DRI" "3"
EndSection
xorg-server built on gentoo with: ./configure --prefix=/usr --build=x86_64-pc-linux-gnu --host=x86_64-pc-linux-gnu --mandir=/usr/share/man --infodir=/usr/share/info --datadir=/usr/share --sysconfdir=/etc --localstatedir=/var/lib --disable-dependency-tracking --disable-silent-rules --docdir=/usr/share/doc/xorg-server-1.20.13-r1 --htmldir=/usr/share/doc/xorg-server-1.20.13-r1/html --with-sysroot=/ --libdir=/usr/lib64 --disable-selective-werror --disable-static --disable-ipv6 --disable-debug --disable-dmx --disable-kdrive --disable-unit-tests --disable-libunwind --enable-record --enable-xfree86-utils --enable-dri --enable-dri2 --enable-dri3 --enable-glamor --enable-glx --disable-xcsecurity --disable-xephyr --disable-xnest --enable-xorg --enable-xvfb --enable-config-udev --without-doxygen --without-xmlto --without-systemd-daemon --disable-xwayland --enable-libdrm --sysconfdir=/etc/X11 --localstatedir=/var --with-fontrootdir=/usr/share/fonts --with-xkb-output=/var/lib/xkb --disable-config-hal --disable-linux-acpi --without-dtrace --without-fop --with-os-vendor=Gentoo --with-sha1=libcrypto CPP=x86_64-pc-linux-gnu-cpp --enable-systemd-logind --disable-install-setuid --disable-suid-wrapper
Yep, that was a mistake. I don't think locking that if statement is even needed. The other thread doesn't modify anything accessed by that check so I think the lock can be put after the if statement.
I've updated my branch accordingly.
tholin . (893c44b2) at 12 Jan 17:57
dix: Hold input lock for AttachDevice()
@p12tic I submitted a merge request with my fix. I've never used gitlab before but hopefully I didn't mess up too badly. A big thanks to you for still maintaining the xserver for us X holdouts.
Fix the following race:
Possible data race during read of size 8 at 0xA112510 by thread #6
Locks held: 1, at address 0x366B40
at 0x14C8B9: GetMaster (devices.c:2691)
by 0x15CFC5: IsFloating (events.c:346)
by 0x2B9554: miPointerGetScreen (mipointer.c:527)
by 0x1A5136: xf86PostButtonEventM (xf86Xinput.c:1379)
by 0x1A52BD: xf86PostButtonEvent (xf86Xinput.c:1345)
by 0x485F45B: EvdevProcessEvent (in /usr/lib64/xorg/modules/input/evdev_drv.so)
by 0x485FDAC: EvdevReadInput (in /usr/lib64/xorg/modules/input/evdev_drv.so)
by 0x195427: xf86ReadInput (xf86Events.c:247)
by 0x2CC113: InputReady (inputthread.c:180)
by 0x2CE4EA: ospoll_wait (ospoll.c:657)
by 0x2CC077: InputThreadDoWork (inputthread.c:369)
by 0x484A336: mythread_wrapper (hg_intercepts.c:406)
This conflicts with a previous write of size 8 by thread #1
Locks held: none
at 0x14D2C6: AttachDevice (devices.c:2609)
by 0x15CF85: ReattachToOldMaster (events.c:1457)
by 0x1647DD: DeactivateKeyboardGrab (events.c:1700)
by 0x25D7F1: ProcXIUngrabDevice (xigrabdev.c:169)
by 0x2552AD: ProcIDispatch (extinit.c:398)
by 0x155291: Dispatch (dispatch.c:479)
by 0x158CBA: dix_main (main.c:276)
by 0x143A3D: main (stubmain.c:34)
Address 0xa112510 is 336 bytes inside a block of size 904 alloc'd
at 0x4846571: calloc (vg_replace_malloc.c:1328)
by 0x14A0B3: AddInputDevice (devices.c:260)
by 0x1A31A0: xf86ActivateDevice (xf86Xinput.c:365)
by 0x1A4549: xf86NewInputDevice (xf86Xinput.c:948)
by 0x1A4B44: NewInputDeviceRequest (xf86Xinput.c:1090)
by 0x1B81FE: device_added (udev.c:282)
by 0x1B8516: config_udev_init (udev.c:439)
by 0x1B7091: config_init (config.c:50)
by 0x197970: InitInput (xf86Init.c:814)
by 0x158C6B: dix_main (main.c:250)
by 0x143A3D: main (stubmain.c:34)
Block was alloc'd by thread #1
The steps to trigger the race are:
Bug report with analysis: #1260
tholin . (a21759ee) at 04 Jan 12:08
dix: Hold input lock for AttachDevice()
tholin . (38291fa8) at 04 Jan 12:05
tholin . (15e299d9) at 04 Jan 12:04
tholin . (15e299d9) at 04 Jan 12:01
Fix the following race:
tholin . (38291fa8) at 04 Jan 11:54
tholin . (09aa5c86) at 04 Jan 11:53
tholin . (09aa5c86) at 04 Jan 11:41
dix: Hold input lock for AttachDevice()
tholin . (38291fa8) at 04 Jan 10:57
When I was searching for race conditions in dix/devices.c I came across this commit.
commit e693c9657f98c334e9921ca2f8ebf710497c0c6a
Author: Arthur Williams <taaparthur@gmail.com>
Date: Sun Oct 6 11:55:35 2019 -0700
dix: Check for NULL spriteInfo in GetPairedDevice
There is a race when reseting the XServer that causes spriteInfo to be
NULL in GetPairedDevice resulting a segfault and subsequent crash. The
problem was noticed when opening a connection, creating master devices,
destroying master devices and closing the connection during testing.
Signed-off-by: Arthur Williams <taaparthur@gmail.com>
diff --git a/dix/devices.c b/dix/devices.c
index 1b18b168e..00c453980 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -2656,7 +2656,7 @@ GetPairedDevice(DeviceIntPtr dev)
if (!IsMaster(dev) && !IsFloating(dev))
dev = GetMaster(dev, MASTER_ATTACHED);
- return dev->spriteInfo->paired;
+ return dev->spriteInfo? dev->spriteInfo->paired: NULL;
}
/**
The commit tries to fix a race condition but I'm not sure the fix is correct.
spriteInfo is modified in a different thread and can presumably become null at any point. The code tries to make sure spriteInfo is not null before dereferencing but the check and access are not atomic. spriteInfo could become null after the check but before the dereferencing. The commit reduced the timing window of the race but it can still crash.
My initial hunch was wrong. mipointer.c:276 only modify a member of pPointer but it's the pPointer itself that is null. Looking over things I now have a new theory.
The crashing null pointer is returned by the macro MIPOINTER which is used heavily in mipointer.c. That macro returns different pointers depending on if the input device is floating or not.
#define MIPOINTER(dev) \
(IsFloating(dev) ? \
(miPointerPtr)dixLookupPrivate(&(dev)->devPrivates, miPointerPrivKey): \
(miPointerPtr)dixLookupPrivate(&(GetMaster(dev, MASTER_POINTER))->devPrivates, miPointerPrivKey))
Floating as far as I understand it means an input device not assigned to a virtual "master" input device. If the main thread were to change the floating status of a device it is important this information gets synchronized to the input thread so it operates on the correct pointer.
Consider this stacktrace:
#0 miPointerDeviceCleanup (pDev=0x559dfd259fe0, pScreen=0x559dfcb78120) at mipointer.c:360
#1 0x0000559dfba3f368 in AttachDevice (client=<optimized out>, dev=dev@entry=0x559dfd259fe0, master=0x559dfcf6c970) at devices.c:2605
#2 0x0000559dfba4ef86 in ReattachToOldMaster (dev=dev@entry=0x559dfd259fe0) at events.c:1457
#3 0x0000559dfba567de in DeactivateKeyboardGrab (keybd=0x559dfd259fe0) at events.c:1700
#4 0x0000559dfbb4f7f2 in ProcXIUngrabDevice (client=0x559dfd892530) at xigrabdev.c:169
#5 0x0000559dfbb472ae in ProcIDispatch (client=0x559dfd892530) at extinit.c:398
#6 0x0000559dfba47292 in Dispatch () at dispatch.c:479
#7 0x0000559dfba4acbb in dix_main (argc=8, argv=0x7ffd6e710b28, envp=<optimized out>) at main.c:276
#8 0x0000559dfba35a3e in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at stubmain.c:34
Here an input grab is deactivated by the main thread causing an input to go from floating to not floating. Nowhere in this stacktrace is the input_lock taken. Below is what the AttachDevice function in devices.c looks like in 1.20.13. My distribution still use that version and I can't see any relevant changes in 21.1.
2577 /**
2578 * Attach device 'dev' to device 'master'.
2579 * Client is set to the client that issued the request, or NULL if it comes
2580 * from some internal automatic pairing.
2581 *
2582 * Master may be NULL to set the device floating.
2583 *
2584 * We don't allow multi-layer hierarchies right now. You can't attach a slave
2585 * to another slave.
2586 */
2587 int
2588 AttachDevice(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr master)
2589 {
2590 ScreenPtr screen;
2591
2592 if (!dev || IsMaster(dev))
2593 return BadDevice;
2594
2595 if (master && !IsMaster(master)) /* can't attach to slaves */
2596 return BadDevice;
2597
2598 /* set from floating to floating? */
2599 if (IsFloating(dev) && !master && dev->enabled)
2600 return Success;
2601
2602 /* free the existing sprite. */
2603 if (IsFloating(dev) && dev->spriteInfo->paired == dev) {
2604 screen = miPointerGetScreen(dev);
2605 screen->DeviceCursorCleanup(dev, screen);
2606 free(dev->spriteInfo->sprite);
2607 }
2608
2609 dev->master = master;
2610
2611 /* If device is set to floating, we need to create a sprite for it,
2612 * otherwise things go bad. However, we don't want to render the cursor,
2613 * so we reset spriteOwner.
2614 * Sprite has to be forced to NULL first, otherwise InitializeSprite won't
2615 * alloc new memory but overwrite the previous one.
2616 */
2617 if (!master) {
2618 WindowPtr currentRoot;
2619
2620 if (dev->spriteInfo->sprite)
2621 currentRoot = GetCurrentRootWindow(dev);
2622 else /* new device auto-set to floating */
2623 currentRoot = screenInfo.screens[0]->root;
2624
2625 /* we need to init a fake sprite */
2626 screen = currentRoot->drawable.pScreen;
2627 screen->DeviceCursorInitialize(dev, screen);
2628 dev->spriteInfo->sprite = NULL;
2629 InitializeSprite(dev, currentRoot);
2630 dev->spriteInfo->spriteOwner = FALSE;
2631 dev->spriteInfo->paired = dev;
2632 }
2633 else {
2634 dev->spriteInfo->sprite = master->spriteInfo->sprite;
2635 dev->spriteInfo->paired = master;
2636 dev->spriteInfo->spriteOwner = FALSE;
2637
2638 XkbPushLockedStateToSlaves(GetMaster(dev, MASTER_KEYBOARD), 0, 0);
2639 RecalculateMasterButtons(master);
2640 }
2641
2642 /* XXX: in theory, the MD should change back to its old, original
2643 * classes when the last SD is detached. Thanks to the XTEST devices,
2644 * we'll always have an SD attached until the MD is removed.
2645 * So let's not worry about that.
2646 */
2647
2648 return Success;
2649 }
The device is floating so the code will "free the existing sprite" by running screen->DeviceCursorCleanup(dev, screen). That call goes to miPointerDeviceCleanup() in mipointer.c:
350 static void
351 miPointerDeviceCleanup(DeviceIntPtr pDev, ScreenPtr pScreen)
352 {
353 SetupScreen(pScreen);
354
355 if (!IsMaster(pDev) && !IsFloating(pDev))
356 return;
357
358 (*pScreenPriv->spriteFuncs->DeviceCursorCleanup) (pDev, pScreen);
359 free(dixLookupPrivate(&pDev->devPrivates, miPointerPrivKey));
360 dixSetPrivate(&pDev->devPrivates, miPointerPrivKey, NULL);
361 }
mipointer.c:360 will set the slave device's miPointerPtr to NULL which is also the data returned by the MIPOINTER macro.
After the cleanup is done devices.c:2609 will set dev->master = master which is what the IsFloating function use to determine the floating status. So the sequence of events are:
Here is another stacktrace where the main thread doesn't take any locks:
#0 miPointerDeviceInitialize (pDev=0x559dfd271f50, pScreen=0x559dfcb78120) at mipointer.c:339
#1 0x0000559dfba3f3a5 in AttachDevice (client=client@entry=0x0, dev=dev@entry=0x559dfd271f50, master=master@entry=0x0) at devices.c:2627
#2 0x0000559dfba4f00c in DetachFromMaster (dev=dev@entry=0x559dfd271f50) at events.c:1443
#3 0x0000559dfba5644a in ActivateKeyboardGrab (keybd=keybd@entry=0x559dfd271f50, grab=grab@entry=0x559dfd9639c0, time=...,
passive=passive@entry=1) at events.c:1633
#4 0x0000559dfba525d7 in ActivatePassiveGrab (device=device@entry=0x559dfd271f50, grab=grab@entry=0x559dfd9639c0,
event=event@entry=0x7ffd6e70fd70, real_event=real_event@entry=0x7ffd6e70fd70) at events.c:3716
#5 0x0000559dfba529c0 in CheckPassiveGrabsOnWindow (pWin=0x559dfcb92d00, device=device@entry=0x559dfd271f50,
event=event@entry=0x7ffd6e70fd70, checkCore=checkCore@entry=0 '\000', activate=activate@entry=1 '\001') at events.c:3964
#6 0x0000559dfba53546 in CheckDeviceGrabs (device=device@entry=0x559dfd271f50, event=event@entry=0x7ffd6e70fd70,
ancestor=ancestor@entry=0x0) at events.c:4046
#7 0x0000559dfbb44b87 in ProcessDeviceEvent (ev=ev@entry=0x7ffd6e70fd70, device=device@entry=0x559dfd271f50) at exevents.c:1778
#8 0x0000559dfbb44d20 in ProcessOtherEvent (ev=0x7ffd6e70fd70, device=0x559dfd271f50) at exevents.c:1868
#9 0x0000559dfbb74860 in ProcessKeyboardEvent (ev=<optimized out>, keybd=0x559dfd271f50) at xkbPrKeyEv.c:178
#10 0x0000559dfbba0e5d in mieqProcessDeviceEvent (dev=dev@entry=0x559dfd271f50, event=event@entry=0x7ffd6e70fd70,
screen=screen@entry=0x559dfcb78120) at mieq.c:491
#11 0x0000559dfbba104f in mieqProcessInputEvents () at mieq.c:551
#12 0x0000559dfba876fa in ProcessInputEvents () at xf86Events.c:151
#13 0x0000559dfba4713c in Dispatch () at dispatch.c:417
#14 0x0000559dfba4acbb in dix_main (argc=8, argv=0x7ffd6e710b28, envp=<optimized out>) at main.c:276
#15 0x0000559dfba35a3e in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at stubmain.c:34
The sequence of events here are:
To verify my theory I used valgrind --tool=helgrind and got a warning like this:
----------------------------------------------------------------
Lock at 0x366B40 was first observed
at 0x48478FF: mutex_trylock_WRK (hg_intercepts.c:1028)
by 0x2CC125: input_force_unlock (inputthread.c:127)
by 0x2D077F: OsResetSignals (utils.c:1330)
by 0x2CDE07: OsInit (osinit.c:318)
by 0x158F3A: dix_main (main.c:154)
by 0x143A3D: main (stubmain.c:34)
Address 0x366b40 is 0 bytes inside data symbol "input_mutex"
Possible data race during read of size 8 at 0xA112510 by thread #6
Locks held: 1, at address 0x366B40
at 0x14C8B9: GetMaster (devices.c:2691)
by 0x15CFC5: IsFloating (events.c:346)
by 0x2B9554: miPointerGetScreen (mipointer.c:527)
by 0x1A5136: xf86PostButtonEventM (xf86Xinput.c:1379)
by 0x1A52BD: xf86PostButtonEvent (xf86Xinput.c:1345)
by 0x485F45B: EvdevProcessEvent (in /usr/lib64/xorg/modules/input/evdev_drv.so)
by 0x485FDAC: EvdevReadInput (in /usr/lib64/xorg/modules/input/evdev_drv.so)
by 0x195427: xf86ReadInput (xf86Events.c:247)
by 0x2CC113: InputReady (inputthread.c:180)
by 0x2CE4EA: ospoll_wait (ospoll.c:657)
by 0x2CC077: InputThreadDoWork (inputthread.c:369)
by 0x484A336: mythread_wrapper (hg_intercepts.c:406)
This conflicts with a previous write of size 8 by thread #1
Locks held: none
at 0x14D2C6: AttachDevice (devices.c:2609)
by 0x15CF85: ReattachToOldMaster (events.c:1457)
by 0x1647DD: DeactivateKeyboardGrab (events.c:1700)
by 0x25D7F1: ProcXIUngrabDevice (xigrabdev.c:169)
by 0x2552AD: ProcIDispatch (extinit.c:398)
by 0x155291: Dispatch (dispatch.c:479)
by 0x158CBA: dix_main (main.c:276)
by 0x143A3D: main (stubmain.c:34)
Address 0xa112510 is 336 bytes inside a block of size 904 alloc'd
at 0x4846571: calloc (vg_replace_malloc.c:1328)
by 0x14A0B3: AddInputDevice (devices.c:260)
by 0x1A31A0: xf86ActivateDevice (xf86Xinput.c:365)
by 0x1A4549: xf86NewInputDevice (xf86Xinput.c:948)
by 0x1A4B44: NewInputDeviceRequest (xf86Xinput.c:1090)
by 0x1B81FE: device_added (udev.c:282)
by 0x1B8516: config_udev_init (udev.c:439)
by 0x1B7091: config_init (config.c:50)
by 0x197970: InitInput (xf86Init.c:814)
by 0x158C6B: dix_main (main.c:250)
by 0x143A3D: main (stubmain.c:34)
Block was alloc'd by thread #1
----------------------------------------------------------------
It looks like the problem I described. The input thread use MIPOINTER in miPointerGetScreen which relies on dev->master to determine the floating status. Meanwhile the main thread is updating dev->master without taking the input lock.
My solution is to slap an input_lock all over AttachDevice. Several other functions in devices.c already use the input_lock.
diff --git a/dix/devices.c b/dix/devices.c
index e7c74d7b7..182d775a3 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -2595,6 +2595,7 @@ AttachDevice(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr master)
if (master && !IsMaster(master)) /* can't attach to slaves */
return BadDevice;
+ input_lock();
/* set from floating to floating? */
if (IsFloating(dev) && !master && dev->enabled)
return Success;
@@ -2639,6 +2640,7 @@ AttachDevice(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr master)
RecalculateMasterButtons(master);
}
+ input_unlock();
/* XXX: in theory, the MD should change back to its old, original
* classes when the last SD is detached. Thanks to the XTEST devices,
* we'll always have an SD attached until the MD is removed.
With this patch the warning in valgrind is silenced but since the crash only happens a few times per year it could take a long time until I know for sure if it works.
As a footnote I should mention that valgrind issued warnings for around 30 other unrelated race conditions all following the same pattern. The input thread wakes up, takes the input_lock and starts processing inputs. Meanwhile the main thread modifies various data structures used by the input thread without taking any lock.