status-notifier-item 0.3.2.8 → 0.3.2.9
raw patch · 4 files changed
+75/−12 lines, 4 filesdep ~optparse-applicativedep ~template-haskellPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: optparse-applicative, template-haskell
API changes (from Hackage documentation)
- StatusNotifier.Host.Service: instance GHC.Show.Show StatusNotifier.Host.Service.ItemInfo
- StatusNotifier.Host.Service: instance GHC.Show.Show StatusNotifier.Host.Service.UpdateType
- StatusNotifier.Item.Service: instance GHC.Read.Read StatusNotifier.Item.Service.ItemParams
- StatusNotifier.Item.Service: instance GHC.Show.Show StatusNotifier.Item.Service.ItemParams
- StatusNotifier.Watcher.Constants: instance GHC.Show.Show StatusNotifier.Watcher.Constants.ItemEntry
- StatusNotifier.Watcher.StateCache: instance GHC.Show.Show StatusNotifier.Watcher.StateCache.JsonValue
- StatusNotifier.Watcher.StateCache: instance GHC.Show.Show StatusNotifier.Watcher.StateCache.PersistedItemEntry
- StatusNotifier.Watcher.StateCache: instance GHC.Show.Show StatusNotifier.Watcher.StateCache.PersistedWatcherState
+ StatusNotifier.Host.Service: instance GHC.Internal.Show.Show StatusNotifier.Host.Service.ItemInfo
+ StatusNotifier.Host.Service: instance GHC.Internal.Show.Show StatusNotifier.Host.Service.UpdateType
+ StatusNotifier.Item.Service: instance GHC.Internal.Read.Read StatusNotifier.Item.Service.ItemParams
+ StatusNotifier.Item.Service: instance GHC.Internal.Show.Show StatusNotifier.Item.Service.ItemParams
+ StatusNotifier.Watcher.Constants: instance GHC.Internal.Show.Show StatusNotifier.Watcher.Constants.ItemEntry
+ StatusNotifier.Watcher.StateCache: instance GHC.Internal.Show.Show StatusNotifier.Watcher.StateCache.JsonValue
+ StatusNotifier.Watcher.StateCache: instance GHC.Internal.Show.Show StatusNotifier.Watcher.StateCache.PersistedItemEntry
+ StatusNotifier.Watcher.StateCache: instance GHC.Internal.Show.Show StatusNotifier.Watcher.StateCache.PersistedWatcherState
Files
- ChangeLog.md +6/−2
- src/StatusNotifier/Host/Service.hs +7/−3
- status-notifier-item.cabal +5/−5
- test/HostSpec.hs +57/−2
ChangeLog.md view
@@ -1,7 +1,11 @@ # Changelog for status-notifier-item -## 0.3.2.8 - 2026-02-13-- Relax dependency bounds for GHC 9.12 (`template-haskell`, `optparse-applicative`).+## 0.3.2.9 - 2026-02-17+- Host: eliminate duplicate `ItemAdded` deliveries to newly-registered update+ handlers by making handler registration + initial replay atomic with respect+ to item map updates.+- Add a deterministic host integration test that reproduces and guards against+ duplicate `ItemAdded` events from replay/live-update races. ## 0.3.2.7 - 2026-02-13 - Watcher: default `--log-level` is now INFO.
src/StatusNotifier/Host/Service.hs view
@@ -158,9 +158,13 @@ addHandler handler = do unique <- newUnique- modifyMVar_ updateHandlersVar (return . ((unique, handler):))- let doUpdateForInfo info = doUpdateForHandler ItemAdded info (unique, handler)- readMVar itemInfoMapVar >>= mapM_ doUpdateForInfo+ modifyMVar_ itemInfoMapVar $ \itemInfoMap -> do+ -- Register and replay under the item map lock so a concurrent add+ -- cannot be delivered both live and via replay.+ modifyMVar_ updateHandlersVar (return . ((unique, handler):))+ let doUpdateForInfo info = doUpdateForHandler ItemAdded info (unique, handler)+ mapM_ doUpdateForInfo itemInfoMap+ return itemInfoMap return unique removeHandler unique =
status-notifier-item.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: status-notifier-item-version: 0.3.2.8+version: 0.3.2.9 synopsis: A wrapper over the StatusNotifierItem/libappindicator dbus specification description: Please see the README on Github at <https://github.com/IvanMalison/status-notifier-item#readme> category: Desktop@@ -53,7 +53,7 @@ , filepath <1.6 , hslogger <1.4 , lens <5.4- , template-haskell <2.24+ , template-haskell <2.23 , text <2.2 , transformers <0.7 , vector <0.14@@ -68,7 +68,7 @@ build-depends: base >=4.7 && <5 , dbus >=1.2.1 && <2- , optparse-applicative <0.20+ , optparse-applicative <0.19 , status-notifier-item default-language: Haskell2010 @@ -80,7 +80,7 @@ ./item build-depends: base >=4.7 && <5- , optparse-applicative <0.20+ , optparse-applicative <0.19 , status-notifier-item default-language: Haskell2010 @@ -95,7 +95,7 @@ , dbus >=1.2.1 && <2 , dbus-hslogger >=0.1.0.1 && <0.2 , hslogger <1.4- , optparse-applicative <0.20+ , optparse-applicative <0.19 , status-notifier-item default-language: Haskell2010
test/HostSpec.hs view
@@ -2,9 +2,19 @@ module HostSpec (spec) where -import Control.Concurrent (newChan, readChan, writeChan)+import Control.Concurrent+ ( forkIO+ , newChan+ , newEmptyMVar+ , putMVar+ , readChan+ , takeMVar+ , threadDelay+ , tryPutMVar+ , writeChan+ ) import Control.Exception (finally)-import Control.Monad (replicateM)+import Control.Monad (replicateM, void) import Data.List (sort) import qualified Data.Map.Strict as Map import DBus (busName_, objectPath_)@@ -218,6 +228,51 @@ current <- itemInfoMap host pure $ not $ Map.member (busName_ "org.test.HostWatcherOwnerChange") current removed `shouldBe` True++ it "does not emit duplicate ItemAdded when addUpdateHandler races with replay" $ \() -> do+ _watcher <- startWatcher+ hostClient <- connectSession+ Just host <- build defaultParams {dbusClient = Just hostClient, uniqueIdentifier = "host-j"}++ itemClient <- connectSession+ buildStarted <- newEmptyMVar+ continueBuild <- newEmptyMVar+ let itemName = "org.test.HostHandlerRace"+ iface =+ Interface+ { interfaceName = "org.kde.StatusNotifierItem"+ , interfaceMethods = []+ , interfaceProperties =+ [ readOnlyProperty "IconName" $ do+ void $ tryPutMVar buildStarted ()+ takeMVar continueBuild+ pure ("folder" :: String)+ , readOnlyProperty "OverlayIconName" (pure ("" :: String))+ , readOnlyProperty "ItemIsMenu" (pure False)+ ]+ , interfaceSignals = []+ }+ export itemClient (objectPath_ defaultPath) iface+ _ <- requestName itemClient (busName_ itemName) []+ WatcherClient.registerStatusNotifierItem itemClient itemName+ `shouldReturn` Right ()++ -- Host is now blocked building the item while holding itemInfoMapVar.+ takeMVar buildStarted++ events <- newChan+ _ <- forkIO $ do+ threadDelay 50000+ putMVar continueBuild ()+ _ <- addUpdateHandler host (\ut info -> writeChan events (ut, itemServiceName info))++ first <- timeout 1500000 (readChan events)+ first `shouldBe` Just (ItemAdded, busName_ itemName)+ second <- timeout 300000 (readChan events)+ second `shouldBe` Nothing++ _ <- releaseName itemClient (busName_ itemName)+ pure () describe "propertyUpdateFailureLogLevel" $ do it "returns DEBUG when there are successful updates" $ do