It is currently April 19th, 2024, 11:44 am

[help] C# and multithreading

Share and get help with Plugins and Addons
User avatar
Brian
Developer
Posts: 2679
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: [help] C# and multithreading

Post by Brian »

FlyingHyrax wrote:Well, I feel rather silly: I totally missed this.
I was going to "chime" into conversation a little earlier, but I thought maybe you might be going in a different direction. Plus, my plugin is written in C++ and not C# (which I am unfamiliar with, so I wasn't much help).


jsmorley wrote:Poiru or Brian may have a better feel for this due to their experiences with threads in C++ in the context of Rainmeter.
I think you are on the right track. Although I don't think Rainmeter will have trouble with spawning a new thread on every Update cycle, I think that will be overkill. In the context of of a Rainmeter plugin, I think threads are only needed to perform "expensive" (or timely) operations that "block" Rainmeter from executing normally.

jsmorley wrote:The flow to me sorta feels like:

Code: Select all

Update()
If UpdateCounter is zero
 Start thread 
 Execute actions
 Thread ends and plugin value is set when actions are finished
Else
 Skip thread and actions
Endif

Increment UpdateCounter
If UpdateCounter is equal to UpdateRate
 Set UpdateCounter to zero
Endif

Return existing or new plugin value from actions.
This is basically how Webparser and some other threaded plugins work when it comes to "when" a thread should be spawned. The trickier part is how to tell the spawned thread that the plugin is no longer loaded; how to assign some value back to the Rainmeter thread (usually using some sort of mutex or critical section); and how to have the spawned thread unload the plugin in case the skin is closed before the spawned thread is finished. It can get very complex quickly, usually requiring more code then you first planned on.

Remember that you need to "lock" any shared data between threads with a mutex or critical section - not just inside your thread.

I wish I could be more help, but I really don't know C# at all. :confused:

-Brian
User avatar
Aragas
Posts: 64
Joined: December 24th, 2012, 6:56 pm

Re: [help] C# and multithreading

Post by Aragas »

So, it's look yet normal.
I forget that i can call simply Dispose() from Plugin.Finialize() so i deleted SkinAlive. All if else methods i switched to switch. It look better. Yet i will make some researches with UpdateRate.

About C# samples. They are poor. They don't have many options, that pluginmaker can need. It would be cool if you would let me put some features in the empty sample. I would highly recommend to change the architecture of all samples too. If you are interested, i can make that in my fork.

UPD:
Plugin will have the same updaterate from skin. How get network connection works? We just run simply 1 time a check. I understand, that we need to make regular checkings. Working on it.
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: [help] C# and multithreading

Post by jsmorley »

Aragas wrote:So, it's look yet normal.
I forget that i can call simply Dispose() from Plugin.Finialize() so i deleted SkinAlive. All if else methods i switched to switch. It look better. Yet i will make some researches with UpdateRate.

About C# samples. They are poor. They don't have many options, that pluginmaker can need. It would be cool if you would let me put some features in the empty sample. I would highly recommend to change the architecture of all samples too. If you are interested, i can make that in my fork.

UPD:
Plugin will have the same updaterate from skin. How get network connection works? We just run simply 1 time a check. I understand, that we need to make regular checkings. Working on it.
Personally, I'd rather not have the example plugins be loaded up with a lot of functionality. They are meant more as a starting point for creating a plugin that has the minimum and required code that is needed to create a plugin. If we want to expand on things, I'd rather do that in some kind of SDK "documentation" that is outside of the plugin examples.
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: [help] C# and multithreading

Post by jsmorley »

Aragas wrote: UPD:
Plugin will have the same updaterate from skin. How get network connection works? We just run simply 1 time a check. I understand, that we need to make regular checkings. Working on it.
This question gives me some concern. Since the Update function in the plugin is executed on every update of the measure in the skin, I would think it is obvious how we are doing "regular checking" of the network connection...

I really don't want to re-invent the wheel here. The code I have does exactly what it needs to do, both from the standpoint of how UpdateRate works, how the "network" checking is done, and how the "internet" checking is done.

Code: Select all

        internal void Reload(Rainmeter.API rm, ref double maxValue)
        {
            ConnectionType = rm.ReadString("ConnectionType", "internet");
            ConnectionType = ConnectionType.ToLowerInvariant();
            if (ConnectionType != "network" && ConnectionType != "internet")
            {
                API.Log(API.LogType.Error, "CheckNet.dll: ConnectionType=" + ConnectionType + " not valid");
            }
            
            UpdateRate = rm.ReadInt("UpdateRate", 20);
            if (UpdateRate <= 0)
            {
                UpdateRate = 20;
            }
        }
This is the part that is executed when the plugin is loaded on skin load or refresh. It simply gets and checks the ConnectionType and UpdateRate options from the skin.

Code: Select all

        internal double Update()
        {
            if (UpdateCounter == 0)
            {
                if (ConnectionType == "network" || ConnectionType == "internet")
                {
                    if (System.Convert.ToDouble(NetworkInterface.GetIsNetworkAvailable()) == 0)
                    {
                        ReturnValue = -1.0;
                    }
                    else
                    {
                        ReturnValue = 1.0;
                    }
                }

                if (ReturnValue == 1.0 && ConnectionType == "internet")
                {
                    try
                    {
                        IPAddress[] addresslist = Dns.GetHostAddresses("www.msftncsi.com");

                        if (addresslist[0].ToString().Length > 6)
                        {
                            ReturnValue = 1.0;
                        }
                        else
                        {
                            ReturnValue = -1.0;
                        }
                    }
                    catch
                    {
                        ReturnValue = -1.0;
                    }
                }
            }

                UpdateCounter = UpdateCounter + 1;
                if (UpdateCounter >= UpdateRate)
                {
                    UpdateCounter = 0;
                }

                return ReturnValue;
        }
Then in the internal double Update() function, that is called on every update of the measure in the skin:

We check to see if UpdateCounter is equal to zero. If it is, we want to execute the rest of the actions in a thread. If not, we will just skip spawning any thread or taking any action.

Start of thread:

We check to be sure that ConnectionType is "network" OR "internet". We then execute the NetworkInterface.GetIsNetworkAvailable() function to check if the network adapter is "up" and talking to a LAN. That sets ReturnValue to 1 on success, and -1 on failure.

Then, but only IF the "network" checking above was successful, we check to see if ConnectionType is "internet". If so, then we use the Dns.GetHostAddresses("www.msftncsi.com") function to test if there is a connection to the internet. That sets ReturnValue to 1 on success, and -1 on failure.

End of thread:

Then we increment UpdateCounter, and if the new value is equal to or greater than UpdateRate, set UpdateCounter to zero.

Then we return ReturnValue to the skin.

I was sorta hoping that we could just wrap some "spawn a thread to run this part" code around the part after it checks if UpdateCounter is equal to zero, and before it increments UpdateCounter at the end.

If we need to entirely re-design the functionality from scratch to accomplish this, then I'm not sure it is worth the effort.
User avatar
Aragas
Posts: 64
Joined: December 24th, 2012, 6:56 pm

Re: [help] C# and multithreading

Post by Aragas »

I understand. I just think that the rainmeter-side code is not good formatted. It's a bit confusing i would say.
Yes, you right. Better expand the documentation. It's very poor. But i insist that my Dispose() method must be in samples. It's pretty useful.

I'm sorry for rewriting the whole code :D My opinion is the old code just was not good for multithreading.
User avatar
Aragas
Posts: 64
Joined: December 24th, 2012, 6:56 pm

Re: [help] C# and multithreading

Post by Aragas »

Ok. I have maked that the internet\network connection information will be refreshed after UpdateCount=0, like you wanted. How about that? I think i have maked all functionality that you have wanted.
Wait...Nope. It updates with skin update. Fix that soon.
Hahe fixed. Now it really updates when count=0.
Wait for you conclusion : D
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: [help] C# and multithreading

Post by jsmorley »

Looks pretty good, but the "internet" check should only be done if the "network" check succeeds.

So the flow of it is:

If ConnectionType is "network" OR "internet"

Check "network"

Set ReturnValue to 1 or -1

If ConnectionType is "network", then we are done. Return ReturnValue

If ConnectionType is "internet" AND the network check has set ReturnValue to "1" (success) then

Check "internet"

Set ReturnValue to 1 or -1

That way, when we fall through to the normal Update() code outside of any threads, the value of ReturnValue will always be either "1" (everything that was asked of it succeeded), "-1" (either the "network" or "internet" checking failed, what is done about that is up to the user of the skin based on which measure returned as "-1"), or "0" (which would mean that an invalid ConnectionType was set in the skin, and no checking at all was done, so ReturnValue remains the default of "0").
User avatar
Aragas
Posts: 64
Joined: December 24th, 2012, 6:56 pm

Re: [help] C# and multithreading

Post by Aragas »

Internet check is doing after network check:

Code: Select all

if (!NetworkInterface.GetIsNetworkAvailable())
            {
                ReturnValue = -1.0;
                return;
            }
Plugin wouldn't make internet check if network check was failed.
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: [help] C# and multithreading

Post by jsmorley »

I think the best way to do this is to put both the "network" and "internet" checking in the same thread. There is just no reason to separate them, as the "network" check needs to be done in ANY case, and the "internet" check (if requested by ConnectionType) is only done if the "network" check succeeds.

It just makes no sense to me to even spawn a separate thread for "internet" since it will just immediately end if the "network" check has failed. They go together in all cases.
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: [help] C# and multithreading

Post by jsmorley »

I also am confused why we are creating and starting the "threads" in the Reload() function of the plugin. Perhaps you can explain that to me, as it looks to me like we are only starting the threads when the skin is loaded or refreshed. I would think we would want to check if UpdateCounter is zero on each Update() and if it is, then start a thread and do the "network" and (if requested) "internet" checking. When the thread has completed its work and set ReturnValue, it just ends.

I may just not understand how you are looking at this. Is it that what we are doing is starting a "thread" when the skin is loaded or refreshed, and it just "keeps running" all the time, only terminating if the skin is unloaded or refreshed?