View Issue Details

IDProjectCategoryView StatusLast Update
0011116MMW v4Framework: Scripts/Extensionspublic2013-08-12 12:29
Reporterzvezdan Assigned To 
PriorityhighSeverityminorReproducibilityalways
Status closedResolutionfixed 
Fixed in Version4.1 
Summary0011116: INI file is not updated until program exit because of some scripts
DescriptionI have problems with scripts by one author because of which storing of settings in my (and any other) scripts to INI file could fail in case of the program crash (as we all know, MM updates INI file only on regular program exit since... oh well, I cannot remember exactly, I think it was build 1196).

Here is my test script:

Option Explicit

Sub OnStartUp()
    Dim oMenuItem

    SDB.UI.AddMenuItemSep SDB.UI.Menu_Edit, -3, 0

    Set oMenuItem = SDB.UI.AddMenuItem(SDB.UI.Menu_Edit, 0, 0)
    oMenuItem.Caption = "Store 0 to INI key"
    oMenuItem.OnClickFunc = "Test1"
    oMenuItem.UseScript = Script.ScriptPath

    Set oMenuItem = SDB.UI.AddMenuItem(SDB.UI.Menu_Edit, 0, 0)
    oMenuItem.Caption = "Store 1 to INI key"
    oMenuItem.OnClickFunc = "Test2"
    oMenuItem.UseScript = Script.ScriptPath
End Sub

Sub Test1(oItem)
    SDB.IniFile.BoolValue("ZvezdanTest", "TestKey") = 0
End Sub

Sub Test2(oItem)
    SDB.IniFile.BoolValue("ZvezdanTest", "TestKey") = 1
End Sub

Here is the problematic part from the script from another user:

Option Explicit

Dim INI : Set INI = SDB.IniFile

Sub OnStartUp()
' Call Script.RegisterEvent(SDB,"OnChangedSelection","SetMnuVisibility")
  Call Script.RegisterEvent(SDB,"OnShutdown","PerformTasksOnShutdown")
End Sub

'Sub SetMnuVisibility
'End Sub

Sub PerformTasksOnShutdown
End Sub

Here are two problematic things that, in combination, prevent storing to INI file in every another script:

1) assigning ISDBIniFile object to the variable on the global level (i.e. not inside of some Sub/Function), and

2) registering some event handler at OnStartup (I didn't try some another event, but OnChangedSelection and/or OnShutdown leads to the problem).
Steps To Reproduce1. Copy both scripts to Auto folder, start program and execute "Store 0 to INI key" command from the Edit menu. Now, open INI file while program is still running and you would see that the corresponding key is not changed (it would not be even created until you restart the program).

2. Remove the second script from the Auto folder, or comment its line Dim INI : Set INI, or comment lines which register event handlers, start the program and execute "Store 0 to INI key" or "Store 1 to INI key" command, open INI file and you would see that the corresponding key is immediately updated.
Additional InformationYes, I know for ISDBIniFile::Flush method, but I bet that nobody is using it in scripts.
TagsNo tags attached.
Fixed in build1652

Activities

Ludek

2013-07-30 12:14

developer   ~0037024

Last edited: 2013-07-30 12:17

Yes, this is how it works, if two scripts works with the same INI file then the file is shared in memory and is flushed (written to disk) once both scripts no longer use the INI, that is why the Flush method was added:
http://www.mediamonkey.com/wiki/index.php/ISDBIniFile::Flush

You can either:
- ask the script creator to not use ISDBIniFile object as global variable
- use own separate ISDBIniFile for your script settings
- use the Flush method for additional safety (If you are afraid that MM can crash and you would lost important INI values)

zvezdan

2013-07-30 13:06

updater   ~0037026

"- ask the script creator to not use ISDBIniFile object as global variable"

I asked the mentioned author, but he refused to do anything about it giving an explanation that declaring INI variable on global level leads to faster execution, which is insignificant in my opinion.

"- use own separate ISDBIniFile for your script settings"

Could you please elaborate? I tried this:
Option Explicit

Dim oIniFile
Set oIniFile = SDB.IniFile

Sub OnStartUp()
    Dim oMenuItem

    SDB.UI.AddMenuItemSep SDB.UI.Menu_Edit, -3, 0

    Set oMenuItem = SDB.UI.AddMenuItem(SDB.UI.Menu_Edit, 0, 0)
    oMenuItem.Caption = "Store 0 to INI key"
    oMenuItem.OnClickFunc = "Test1"
    oMenuItem.UseScript = Script.ScriptPath

    Set oMenuItem = SDB.UI.AddMenuItem(SDB.UI.Menu_Edit, 0, 0)
    oMenuItem.Caption = "Store 1 to INI key"
    oMenuItem.OnClickFunc = "Test2"
    oMenuItem.UseScript = Script.ScriptPath
End Sub

Sub Test1(oItem)
' SDB.IniFile.BoolValue("ZvezdanTest", "TestKey") = 0
    oIniFile.BoolValue("ZvezdanTest", "TestKey") = 0
End Sub

Sub Test2(oItem)
' SDB.IniFile.BoolValue("ZvezdanTest", "TestKey") = 1
    oIniFile.BoolValue("ZvezdanTest", "TestKey") = 1
End Sub

and this:
Option Explicit

Sub OnStartUp()
    Dim oMenuItem

    SDB.UI.AddMenuItemSep SDB.UI.Menu_Edit, -3, 0

    Set oMenuItem = SDB.UI.AddMenuItem(SDB.UI.Menu_Edit, 0, 0)
    oMenuItem.Caption = "Store 0 to INI key"
    oMenuItem.OnClickFunc = "Test1"
    oMenuItem.UseScript = Script.ScriptPath

    Set oMenuItem = SDB.UI.AddMenuItem(SDB.UI.Menu_Edit, 0, 0)
    oMenuItem.Caption = "Store 1 to INI key"
    oMenuItem.OnClickFunc = "Test2"
    oMenuItem.UseScript = Script.ScriptPath
End Sub

Sub Test1(oItem)
' SDB.IniFile.BoolValue("ZvezdanTest", "TestKey") = 0
    Dim oIniFile

    Set oIniFile = SDB.IniFile
    oIniFile.BoolValue("ZvezdanTest", "TestKey") = 0
End Sub

Sub Test2(oItem)
' SDB.IniFile.BoolValue("ZvezdanTest", "TestKey") = 1
    Dim oIniFile

    Set oIniFile = SDB.IniFile
    oIniFile.BoolValue("ZvezdanTest", "TestKey") = 1
End Sub

and I got the same result, i.e. settings are not saved to the INI file immediately.

"- use the Flush method for additional safety (If you are afraid that MM can crash and you would lost important INI values)"

I am not speaking only because of me. All scripts are affected, and I didn't see any script by another authors which use Flush method. Besides, I have some scripts that are saving INI variables very often, and using Flush method could degrade the performance.

Could you tell me why registering some MM event is essential for such behavior? I understand that declaring global INI variable in one script could lead to this, but why INI settings are saved if such script has not registering events?

Ludek

2013-07-30 14:48

developer   ~0037028

Last edited: 2013-07-30 14:53

>Could you please elaborate?

I meant that if the "ZvezdanTest", "TestKey" is value related to your script only, then you can crete own INI file for storing such a values by using SDB.Tools.IniFileByPath( MyIniFilePath) that will be accessed exclusivelly by your script.


>Could you tell me why registering some MM event is essential for such behavior?

This is not driven by MediaMonkey, but by OLE automation / VB scripting engine. It tells MM that the ISDBIniFile interface is no longer used and can be destroyed.

But as you said, the user wants to have INI file as global variable and keep it in memory to improve performance.

We could perform Flush on MediaMonkey.ini internally on a regural basic (e.g. every minute or so), but I am not sure that it is really needed. Nevertheless I can add it if you see it as important.

zvezdan

2013-07-30 15:20

updater   ~0037029

> you can crete own INI file for storing such a values

Oh, I see. Unfortunately, it is not an option for me. I don't want to store settings of all my scripts outside of MediaMonkey.ini just because of scripts from some another author. And I need to repeat, it was not only my scripts that are affected, but everyone.

> It tells MM that the ISDBIniFile interface is no longer used and can be destroyed.

Sorry, but I don't understand. Registering of MM events is done during OnStartup. How is ISDBIniFile used in the another author's script if it is destroyed on startup?

Here is your first explanation: "if two scripts works with the same INI file then the file is shared in memory and is flushed (written to disk) once both scripts no longer use the INI". So, how is it possible that this apply only if the problematic script has registered MM event on startup? Or, taking it from another side, why your explanation doesn't apply if such script doesn't have registering of MM events?

zvezdan

2013-07-30 15:44

updater   ~0037030

Last edited: 2013-07-30 15:47

It was long ago when you decided to store values to INI file only on regular exit, but not after every BoolValue/StringValue/IntValue Let method, and I remember that jiri explained this your decision in forum. Also, I recall that he said how INI values would not be stored immediately to the INI file only if *Value method is applied to IniFile object declared as variable, i.e. this will not store INI key immediately:
oIniFile.BoolVariable("...", "...") = ...

but this will do that:
SDB.IniFile.BoolVariable("...", "...") = ...

I cannot find his post to quote it, but I am 100% sure that he said something like that, and that was a reason why I am always using such syntax in my scripts when I want to store some value to the INI file. So, why that doesn't apply with the mentioned problematic scripts?

zvezdan

2013-07-30 15:59

updater   ~0037031

> We could perform Flush on MediaMonkey.ini internally on a regural basic (e.g. every minute or so), but I am not sure that it is really needed. Nevertheless I can add it if you see it as important.

I think it is important because many users reported to me how my scripts are not stored settings to INI file, and I discovered that it was because of problematic scripts and program crush.

I think you don't need to flush to INI internally every minute, but you could create a timer when some script have applied some *Value Let method to the IniFile object. That timer could have even shorter interval, e.g. 1-2 seconds, but if some scripts execute several INI assignments in succession, you would apply Flush only on the last one. In that case you would not apply Flush every minute when none script is storing values to INI file.

zvezdan

2013-07-30 16:17

updater   ~0037032

I just want to mention, the INI settings of Magic Nodes and RegExp Find & Replace are not so simple. You know, if some user ticks some check box in the Options dialog box and if that setting is not remembered because of program crush, it is not a big deal - he would just tick that check box again.

However, users could write very complicated Magic Nodes masks, even with SQL filters, also they could write complicated RegExp presets using Regular expressions and VBScript expressions contained in them. So, could you imagine their frustration if such settings are not stored in INI file after who-knows-how-many minutes of work just because of the mentioned problem.

Ludek

2013-07-31 20:39

developer   ~0037040

OK, every 30 seconds all opened and modified INI files are flushed.

Fixed in build 1652.

zvezdan

2013-07-31 21:02

updater   ~0037041

Many thanks! But I am just wondering, wouldn't be better (if it is possible) to flush to INI file only 30 seconds after the last applied BoolValue/StringValue/IntValue method from any script? I think that in such case it would consume less resources, instead of flushing to INI file every 30 seconds even if none script is using the mentioned methods in meantime.

Ludek

2013-07-31 22:23

developer   ~0037042

Last edited: 2013-07-31 22:27

If none script is using the mentioned methods in meantime then anything is flushed. The INI needs to be modified before MM auto-flushes it. You can test it in 1652.

zvezdan

2013-08-01 05:41

updater   ~0037047

Yes, I understood that, even from your previous note ("modified INI files are flushed"). However, my question is not: do you flush to INI file every 30 seconds, but: do you test the condition for flush every 30 seconds? You know, if you compare some variables every 30 seconds, it is time and resource consuming, not matter how much. I just suggested that your new timer fire only on the last applied mentioned methods with the given delay, but not every 30 seconds.

peke

2013-08-04 02:26

developer   ~0037085

Re opening for Zvezdan 1652 test and Triage

zvezdan

2013-08-04 13:23

updater   ~0037091

I tried 1652 and the INI is indeed flushed after some modification, which is great. However, I don't know how you did it internally and I am just wondering which approach do you have. Here is the pseudo-code of your program as I think that you have it implemented:

Sub OnStartup
    Set oFlushTimer = SDB.CreateTimer(30000)
    Script.RegisterEvent oFlushTimer, "OnTimer", "OnFlushTimer"
    oFlushTimer.Enabled = True
End Sub

Sub OnFlushTimer(oTimer)
    If flush_condition = True Then
        SDB.IniFile.Flush
    End If
End Sub

Here is the pseudo-code of my suggestion:

Sub OnStartup
    Set oFlushTimer = SDB.CreateTimer(30000)
    Script.RegisterEvent oFlushTimer, "OnTimer", "OnFlushTimer"
    oFlushTimer.Enabled = False ' timer should not execute periodically
End Sub

Sub OnScriptCommandExecuted ???
    If script_command = "SDB.IniFile.BoolValue" Then
        oFlushTimer.Enabled = False ' this line is to reset timer to 0
        oFlushTimer.Enabled = True ' with this we start delay of flush
    End If
End Sub

Sub OnFlushTimer(oTimer)
    oFlushTimer.Enabled = False ' disable timer until next *Value command
    If flush_condition = True Then
        SDB.IniFile.Flush
    End If
End Sub

With my suggestion the OnFlushTimer event handler is not executed every 30 seconds, but only 30 seconds after the last executed IniFile.*Value command. However, it was just my guessing because I actually don't know if you have in your code any way to detect when some IniFile.*Value command is executed and to respond to that.

Anyway, it is now much better than it was before, and I am grateful for what you did. Thanks again!

Ludek

2013-08-12 12:28

developer   ~0037131

Don't worry, it is implemented internally with a really minimal performance impact as all opened INI files are kept in memory and checking its 'modified' private property is a simple instruction.

Closing...