-
-
Notifications
You must be signed in to change notification settings - Fork 343
Fix infinite memory usage by allowing old data to be garbage collected #2473
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
base: master
Are you sure you want to change the base?
Changes from all commits
243f88c
2a884c8
9d0684d
82bbc97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,7 +31,6 @@ import Hledger.UI.UIUtils | |
| import Hledger.UI.UIScreens | ||
| import Hledger.UI.Editor | ||
| import Hledger.UI.ErrorScreen (uiCheckBalanceAssertions, uiReload, uiReloadIfFileChanged) | ||
| import Hledger.UI.RegisterScreen (rsHandle) | ||
|
|
||
| tsDraw :: UIState -> [Widget Name] | ||
| tsDraw UIState{aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}} | ||
|
|
@@ -191,47 +190,9 @@ tsHandle ev = do | |
| -- XXX On transaction screen or below, this is tricky because of a current limitation of regenerateScreens. | ||
| -- For now we try to work around by re-entering the screen(s). | ||
| -- This can show flicker in the UI and it's hard to handle all situations robustly. | ||
| tsReload copts d ui = uiReload copts d ui >>= reEnterTransactionScreen copts d | ||
| tsReloadIfFileChanged copts d j ui = liftIO (uiReloadIfFileChanged copts d j ui) >>= reEnterTransactionScreen copts d | ||
|
|
||
| reEnterTransactionScreen _copts d ui = do | ||
| -- 1. If uiReload (or checking balance assertions) moved us to the error screen, save that, and return to the transaction screen. | ||
| let | ||
| (merrscr, uiTxn) = case aScreen $ uiCheckBalanceAssertions d ui of | ||
| s@(ES _) -> (Just s, popScreen ui) | ||
| _ -> (Nothing, ui) | ||
| -- 2. Exit to register screen | ||
| let uiReg = popScreen uiTxn | ||
| put' uiReg | ||
| -- 3. Re-enter the transaction screen | ||
| rsHandle (VtyEvent (EvKey KEnter [])) -- PARTIAL assumes we are on the register screen. | ||
| -- 4. Return to the error screen (below the transaction screen) if there was one. | ||
| -- Next events will be handled by esHandle. Error repair will return to the transaction screen. | ||
| maybe (return ()) (put' . flip pushScreen uiTxn) merrscr | ||
| -- doesn't uiTxn have old state from before step 3 ? seems to work | ||
|
|
||
| -- XXX some problem: | ||
| -- 4. Reload once more, possibly re-entering the error screen, by sending a g event. | ||
| -- sendVtyEvents [EvKey (KChar 'g') []] -- XXX Might be disrupted if other events are queued | ||
|
|
||
| -- XXX doesn't update on non-error change: | ||
| -- 4. Reload once more, possibly re-entering the error screen. | ||
| -- uiTxnOrErr <- uiReload copts d uiTxn | ||
| -- uiReloadIfChanged ? | ||
| -- uiCheckBalanceAssertions ? seems unneeded | ||
| -- put' uiTxnOrErr | ||
|
|
||
| -- XXX not working right: | ||
| -- -- 1. If uiReload (or checking balance assertions) moved us to the error screen, exit to the transaction screen. | ||
| -- let | ||
| -- uiTxn = case aScreen $ uiCheckBalanceAssertions d ui of | ||
| -- ES _ -> popScreen ui | ||
| -- _ -> ui | ||
| -- -- 2. Exit to register screen | ||
| -- put' $ popScreen uiTxn | ||
| -- -- 3. Re-enter the transaction screen, and reload once more. | ||
| -- sendVtyEvents [EvKey KEnter [], EvKey (KChar 'g') []] -- XXX Might be disrupted if other events are queued | ||
| tsReload copts d ui = uiReload copts d ui >>= put' | ||
|
|
||
| tsReloadIfFileChanged copts d j ui = liftIO (uiReloadIfFileChanged copts d j ui) >>= put' | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This special handling for regenerating transaction screen was added in 1.50.1 to fix #2288. It has been removed and I think not fully replaced by later commits ? Also is the comment above still accurate ?
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PS a little more context, my understanding was that the right fix for #2288 would be to make regenerateScreens take parent screens' state into account (fold rather than map). |
||
|
|
||
| -- | Select a new transaction and update the previous register screen | ||
| tsSelect :: Integer -> Transaction -> UIState -> UIState | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,7 +63,7 @@ screenUpdate opts d j = \case | |
| BS sst -> BS $ bsUpdate opts d j sst | ||
| IS sst -> IS $ isUpdate opts d j sst | ||
| RS sst -> RS $ rsUpdate opts d j sst | ||
| TS sst -> TS $ tsUpdate sst | ||
| TS sst -> TS $ tsUpdate opts d j sst | ||
| ES sst -> ES $ esUpdate sst | ||
|
|
||
| -- | Construct an error screen. | ||
|
|
@@ -241,11 +241,12 @@ rsNew uopts d j acct forceinclusive = -- XXX forcedefaultselection - whether to | |
| } | ||
|
|
||
| -- | Update a register screen from these options, reporting date, and journal. | ||
| rsUpdate :: UIOpts -> Day -> Journal -> RegisterScreenState -> RegisterScreenState | ||
| rsUpdate uopts d j rss@RSS{_rssAccount, _rssForceInclusive, _rssList=oldlist} = | ||
| dbgui "rsUpdate" | ||
| rsUpdate uopts d j rss@RSS{_rssAccount, _rssForceInclusive} = | ||
| dbgui "rsUpdate" $ | ||
| rss{_rssList=l'} | ||
| where | ||
| -- Force evaluation of old list to allow GC | ||
| !oldlist = _rssList rss | ||
| UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}} = uopts | ||
| -- gather arguments and queries | ||
| -- XXX temp | ||
|
|
@@ -279,7 +280,8 @@ rsUpdate uopts d j rss@RSS{_rssAccount, _rssForceInclusive, _rssList=oldlist} = | |
| items | ||
|
|
||
| -- pre-render the list items, helps calculate column widths | ||
| displayitems = map displayitem items' | ||
| -- Force evaluation to prevent thunk accumulation | ||
| !displayitems = map displayitem items' | ||
| where | ||
| displayitem (t, _, _issplit, otheraccts, change, bal) = | ||
| RegisterScreenItem{rsItemDate = showDate $ transactionRegisterDate wd (_rsQuery rspec') thisacctq t | ||
|
|
@@ -297,7 +299,7 @@ rsUpdate uopts d j rss@RSS{_rssAccount, _rssForceInclusive, _rssList=oldlist} = | |
|
|
||
| -- blank items are added to allow more control of scroll position; we won't allow movement over these. | ||
| -- XXX Ugly. Changing to 0 helps when debugging. | ||
| blankitems = replicate uiNumBlankItems | ||
| !blankitems = replicate uiNumBlankItems | ||
| RegisterScreenItem{rsItemDate = "" | ||
| ,rsItemStatus = Unmarked | ||
| ,rsItemDescription = "" | ||
|
|
@@ -308,15 +310,16 @@ rsUpdate uopts d j rss@RSS{_rssAccount, _rssForceInclusive, _rssList=oldlist} = | |
| } | ||
|
|
||
| -- build the new list widget | ||
| l = list RegisterList (V.fromList $ displayitems ++ blankitems) 1 | ||
| !itemsVector = V.fromList $ displayitems ++ blankitems | ||
| !l = list RegisterList itemsVector 1 | ||
|
|
||
| -- ensure the appropriate list item is selected: | ||
| -- if forcedefaultselection is true, the last (latest) transaction; XXX still needed ? | ||
| -- otherwise, the previously selected transaction if possible; | ||
| -- otherwise, the transaction nearest in date to it; | ||
| -- or if there's several with the same date, the nearest in journal order; | ||
| -- otherwise, the last (latest) transaction. | ||
| l' = listMoveTo newselidx l | ||
| !l' = listMoveTo newselidx l | ||
| where | ||
| endidx = max 0 $ length displayitems - 1 | ||
| newselidx = | ||
|
|
@@ -357,10 +360,17 @@ tsNew acct nts nt = | |
| ,_tssTransaction = nt | ||
| } | ||
|
|
||
| -- | Update a transaction screen. | ||
| -- This currently does nothing because the initialisation in rsHandle is not so easy to extract. | ||
| -- To see the updated transaction, one must exit and re-enter the transaction screen. | ||
| -- See also tsHandle. | ||
| tsUpdate :: TransactionScreenState -> TransactionScreenState | ||
| tsUpdate = dbgui "tsUpdate" | ||
|
|
||
| -- | Update a transaction screen by refreshing the current transaction from the journal. | ||
| -- This preserves the current transaction selection while updating its data. | ||
| -- XXX Caveat, this works by showing the transaction at the same index in the processed ledger, | ||
| -- if transactions have been inserted or removed before the one shown this will just show | ||
| -- whatever transaction landed at the same index in the new data set. | ||
| tsUpdate :: UIOpts -> Day -> Journal -> TransactionScreenState -> TransactionScreenState | ||
| tsUpdate _ _ j tss@TSS{_tssTransaction=(currentIdx,currentTxn)} = | ||
| dbgui "tsUpdate" $ | ||
| let | ||
| -- Find the updated version of the current transaction in the journal | ||
| updatedTxn = case find (\t -> tindex t == tindex currentTxn) (jtxns j) of | ||
| Just t -> t | ||
| Nothing -> currentTxn -- fallback to current if not found | ||
| in tss { _tssTransaction = (currentIdx, updatedTxn) } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this replace the special regeneration previously in TransactionScreen.hs ? |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,7 +59,7 @@ import Hledger | |
| import Hledger.Cli.CliOptions | ||
| import Hledger.UI.UITypes | ||
| import Hledger.UI.UIOptions (UIOpts(uoCliOpts)) | ||
| import Hledger.UI.UIScreens (screenUpdate) | ||
| import Hledger.UI.UIScreens | ||
| import Hledger.UI.UIUtils (showScreenId, showScreenStack) | ||
|
|
||
| -- | Make an initial UI state with the given options, journal, | ||
|
|
@@ -366,8 +366,18 @@ resetScreens d ui@UIState{astartupopts=origopts, ajournal=j, aScreen=s,aPrevScre | |
| -- (using the ui state's current options), preserving the screen navigation history. | ||
| -- Note, does not save the reporting date. | ||
| -- | ||
| -- XXX Currently this does not properly regenerate the transaction screen or error screen, | ||
| -- which depend on state from their parent(s); those screens' handlers must do additional work, which is fragile. | ||
| -- XXX Currently this does not properly regenerate or error screen, | ||
| -- which depends on state from their parent(s); that screens' handler must do additional work, which is fragile. | ||
| regenerateScreens :: Journal -> Day -> UIState -> UIState | ||
| regenerateScreens j d ui@UIState{aopts=opts, aScreen=s,aPrevScreens=ss} = | ||
| ui{ajournal=j, aScreen=screenUpdate opts d j s, aPrevScreens=map (screenUpdate opts d j) ss} | ||
| regenerateScreens j d ui@UIState{aScreen=s} = | ||
| let !ui' = ui{ajournal=j, aScreen=s'} | ||
| !s' = case s of | ||
| MS mss -> MS $! msUpdate mss | ||
| AS ass -> AS $! asUpdate (aopts ui') d j ass | ||
| CS ass -> CS $! csUpdate (aopts ui') d j ass | ||
| BS ass -> BS $! bsUpdate (aopts ui') d j ass | ||
| IS ass -> IS $! isUpdate (aopts ui') d j ass | ||
| RS rss -> RS $! rsUpdate (aopts ui') d j rss | ||
| TS tss -> TS $! tsUpdate (aopts ui') d j tss | ||
| ES _ -> s | ||
| in ui' | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Screen types and functions are listed explicitly in a bunch of places, so maybe it's no great loss to do that here also - but is it possible to achieve the same strictness without listing them all ? More seriously, doesn't it now only regenerate the current screen ?
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Comments so far are based on code review, I haven't tested yet. Unfortunately it's easy to break behaviours in hledger-ui in ways that currently are hard to test, except manually.) |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reloading in the register screen now has an extra call to regenerateScreens. Does this help ? I think it's also being called by uiReload. Needs an explanatory comment at least.