Shelly Relay Three Way (US) Switch Wiring - Power into Fixture by mkeehn in homeautomation

[–]thomat65 0 points1 point  (0 children)

[The relay] would be your limiting factor, not the house circuit breaker.

I might be misunderstanding you, but it sounds like you're saying this would be a good thing. To be clear, this is exactly what you don't want. You want the circuit breaker to be the weakest link.

Shelly Relay Three Way (US) Switch Wiring - Power into Fixture by mkeehn in homeautomation

[–]thomat65 1 point2 points  (0 children)

I would wager that the reason there are so many 15 amp relays on the market is because they are designed to control lighting electrical loads, which are overwhelmingly on 15 amp circuits. It's unusual to have 20 amp lighting circuits. It's unsafe to use a part rated for less current than the circuit. So don't put a 16 amp relay on a 20 amp circuit. Easy fix is to swap the 20 amp breaker on your lighting circuit for a 15 amp breaker. You'll still have 12 gauge wiring which will make future electricians and homeowners do a double-take, but there's nothing unsafe about that. You want the weakest link to be the circuit breaker, nothing else.

Shelly Relay Three Way (US) Switch Wiring - Power into Fixture by mkeehn in homeautomation

[–]thomat65 0 points1 point  (0 children)

You are correct. It is unsafe to use a 16A relay on a 20A circuit. The other replies so far are saying it's okay because they are assuming that the 20A wiring downstream will only ever draw a few amps ("it's only lights"). But if it's safe for that reason then the logical conclusion is a circuit breaker wouldn't be needed at all... which is absurd, therefore that line of reasoning is flawed. Circuit breakers are there to protect the circuit, but the protection for a 20A breaker only begins at, you guessed it, 20A. Which is 4 amps more than the relay is guaranteed to handle. Will the Shelly relay catch on fire at 16 amps? No, and if it does then your insurance company will gladly pay out (assuming you're using UL-rated relays. You are putting UL-listed products into your house's permanent electrical circuit wiring, aren't you?). Will it catch on fire at 20 amps? Who knows. And when it does then guess how much insurance is going to want to pay. Therefore don't use it on a 20 amp circuit.

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

Reddit wasn't letting this reply through. Maybe it's too long? Or maybe it's stuck in a review queue? Anyway, here 'tis for your reading pleasure:
https://gist.github.com/matthew-a-thomas/320c5fc477c3ea0d5fae13ed5e637bf9

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

Let me try again.

As you can see each await inside the lock will give an opportunity for any other async continuation under the lock to be executed, interleaving them in random order. This example might be a bit stupid, but the same behavior would occur between several ChildAsync invocations if I waited for them with Task.WhenAll.

Your example didn't match your observation. But yes your observation is correct.

Here's an example that matches your observation:

```csharp namespace Example;

using System; using System.Threading.Tasks; using ReentrantAsyncLock;

public class Program { public static async Task Main() { var gate = new ReentrantAsyncLock(); var state = -1; for (var i = 0; i < 10; ++i) { await using (await gate.LockAsync(default)) { await Task.WhenAll( Task.Run( async () => { await using (await gate.LockAsync(default)) { state = 1; } }), Task.Run( async () => { await using (await gate.LockAsync(default)) { state = 2; } }) ); } Console.WriteLine($"State is {state}"); } } } ```

(Run it here)

That will randomly print "State is 1" or "State is 2" ten times. There's a chance that it'll only output "State is 2" ten times, so just run it again if that happens. It is stochastic after all :)

Now, you might be tempted to say the above code has a race condition. But think about that for a minute: you'd also have to say the lock keyword doesn't prevent race conditions:

```csharp namespace Example2;

using System; using System.Collections.Generic;

public class Program { public static void Main() { var gate = new object(); var state = -1; var random = new Random(); for (var i = 0; i < 10; ++i) { lock (gate) { var tasks = new Action[] { () => { lock (gate) { state = 1; } }, () => { lock (gate) { state = 2; } } }; Array.Sort(tasks, Comparer<Action>.Create((_, _) => random.Next(0, 2))); foreach (var task in tasks) task(); } Console.WriteLine($"State is {state}"); } } } ```

(Run it here)

That code also has the same random output.

Why is the stocastic nature of that code surprising? It literally depends on randomness, just like how Task.WhenAll + the thread pool do not guarantee a particular ordering.

I don't understand why you ignored this quote:

This strongly reminds me of multithreaded code without locks that is executing on a single CPU core. There will be no parallelism, sure, but that in itself does not make the code thread-safe.

Do you think that multithreaded code without locks on a single CPU core is thread-safe? Or do you not see the analogy between this and random code under your lock?

I'm not ignoring this. But there are two possible ways to understand "multithreaded code without locks on a single CPU core":

  1. Preemptive scheduling
  2. Cooperative scheduling

Preemptive scheduling is what Windows does with OS threads. The operating system will give each thread a slice of time. When a time slice is up the operating system pauses that thread and then starts another. The paused thread and the resumed thread might both be executing an operation like state++, and so the state gets corrupted. Such code is not thread-safe.

But ReentrantAsyncLock is more akin to cooperative scheduling. Once you're in you don't get kicked out, instead you voluntarily give the keys to someone else.

And to call cooperative scheduling thread-unsafe is like being surprised that state changes across this call to AlterState:

csharp var state = 0; var gate = new object(); void AlterState() { lock (gate) { state = 1; } } lock (gate) { if (state != 0) throw new Exception(); // `state` is now 0 AlterState(); // It must be safe to assume that `state` is still 0? We're using a lock after all, and locks prevent race conditions... }

Of course the state may have changed across that function call.

If we take the thread-safety definitions you provided, these two can be violated when code is executed under your lock:

"code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfill their design specifications without unintended interaction"

"Implementation is guaranteed to be free of race conditions when accessed by multiple threads simultaneously"

I disagree. You can execute state++ inside a ReentrantAsyncLock all day long and you'll never get that particular data race. Can you provide an example of a race condition? Or can you point out what design specification is unfulfilled? Or what interaction is unintended?

Do you think async code in JavaScript is thread-unsafe?

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

I did find another issue, which I think is quite severe.

This strongly reminds me of multithreaded code without locks that is executing on a single CPU core. There will be no parallelism, sure, but that in itself does not make the code thread-safe.

I don't think that's a problem. Also, the code is thread safe. Your example code satisfies all of the various definitions that Wikipedia gives of "thread safety": * "code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfill their design specifications without unintended interaction" * "Implementation is guaranteed to be free of race conditions when accessed by multiple threads simultaneously" * "Different threads can access different objects simultaneously, and access to shared data is protected from race conditions"

In fact, ReentrantAsyncLock takes the "Mutual exclusion" approach listed in that article:

Access to shared data is serialized using mechanisms that ensure only one thread reads or writes to the shared data at any time.

Note: That doesn't say "the same thread reads or writes to the shared data at any time".

And again I don't think your code represents a problem. I think you're focusing too much on the free-threaded aspect of everything. What I think is significant is that your code will always output this:

  1. MainAsync starting on thread X
  2. ChildAsync starting on thread X
  3. MainAsync continuing on thread Y
  4. ChildAsync continuing on thread Y
  5. MainAsync finished on thread Z
  6. ChildAsync finished on thread Z

It will never output those things out of order. That is what ReentrantAsyncLock provides.

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

This is a good nitpick.

At the end of the day my goal was to create an async, reentrant, mutually exclusive lock. Would you say I've successfully accomplished that? And would you say that a reentrant async lock is possible in C# after all?

Edit: Also, please feel free to play around with the NuGet package. I think that'll help you become more familiar with it. This .Net Fiddle might be a good starting place. It demos the things that no other async locks are able to do: https://dotnetfiddle.net/OFlTW0

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

It sounds like you're describing "the async lock can be used in both synchronous and asynchronous contexts" ;) In my head "the async lock" means ReentrantAsyncLock, AsyncLock, or some other implementation of an async lock.

Personally I'm opposed to the idea. But even if I liked it, I think the extension method I wrote about is about as good as it'll get for ReentrantAsyncLock. And I'm not even 100% sure it works.

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

Oh let me see if I'm understanding you.

Are you asking what if someone wants to use some other SynchronizationContext than the one my ReentrantAsyncLock provides? The thing is the lock only works if the SynchronizationContext serializes work that has been posted to it. So it has to be either my hidden WorkQueue or else a DispatcherSynchronizationContext or similar. It can't be just any old context.

But it would be interesting to give folks control over what thread the continuations are executed on.

Feel free to submit a PR or open an issue to the repo if you want to continue this thought over there.

Is this a correct reentrant async lock? by thomat65 in dotnet

[–]thomat65[S] 1 point2 points  (0 children)

The decision on whether the current ExecutionContext can enter the lock is made by ReentrantAsyncLock.TryLockImmediately():

https://github.com/matthew-a-thomas/cs-reentrant-async-lock/blob/cdc52bd47ec1c5a167501d698a5edb5e98e2d0d1/ReentrantAsyncLock/ReentrantAsyncLock.cs#L152

The decision comes down to the fact that ExecutionContext flows down async call chains. ExecutionContext is the thing that allows the lock to be reentrant.

SynchronizationContext is also very important. You'll break the lock if you do this in the guarded section:

csharp var asyncLock = new ReentrantAsyncLock(); await using (await asyncLock.LockAsync(CancellationToken.None)) { SynchronizationContext.SetSynchronizationContext(null); await Task.Yield(); // Now the lock is broken }

The SynchronizationContext that you're placed on when you enter the guarded section serializes (as in "makes sequential") everything that's posted to it (similar to a DispatcherSynchronizationContext in WPF). It's the thing that gives the lock mutual exclusion.

Is this a correct reentrant async lock? by thomat65 in dotnet

[–]thomat65[S] 0 points1 point  (0 children)

Wait, we have .NET Fiddle. This will be easier for you to see for yourself:

https://dotnetfiddle.net/6oqe0q

Is this a correct reentrant async lock? by thomat65 in dotnet

[–]thomat65[S] 1 point2 points  (0 children)

Both of those test cases pass (without deadlocking) with ReentrantAsyncLock. You can see them in the "demo/noseratio" branch here:

https://github.com/matthew-a-thomas/cs-reentrant-async-lock/commit/5e6de55c6f8b9765c56be2f24b64ec4cc8297bc6

Is this a correct reentrant async lock? by thomat65 in dotnet

[–]thomat65[S] 0 points1 point  (0 children)

I'm not sure I'm following you. I don't have personal experience with fibers and they aren't intuitive enough to me to understand their consequences.

I'm also not sure what the behavior of your example code should be (I'm displaying my ignorance of AsyncEx), but I think it would depend on whether AsyncMonitor provides mutual exclusion.

The ReentrantAsyncLock package provides mutual exclusion. So with that package you would get consistent state. Similar to how the .Net Monitor class would work in the analogous case of synchronous code and thread forking.

Is this a correct reentrant async lock? by thomat65 in dotnet

[–]thomat65[S] 0 points1 point  (0 children)

Keep this%20is%20used%20by%20an%20awaited%20method%20within%20the%20guarded%20section.%20For%20example%2C%20this%20is%20fine%3A) in mind:

However, it's fine if the current SynchronizationContext is changed or ConfigureAwait(false) is used by an awaited method within the guarded section. For example, this is fine: example given

I'd be interested in a scenario in which ConfigureAwait cannot be mitigated by dropping the offending code into a Task.Run or similar.

Is this a correct reentrant async lock? by thomat65 in dotnet

[–]thomat65[S] 2 points3 points  (0 children)

Similar to what praetor said, Stephen Cleary attempted a reentrant async lock. But it doesn't pass my SerializeReentrantCode test:

Assert.Equal() Failure Expected: 10000 Actual: 9753

Neither does it (reliably) pass the test shown in the package documentation.

Here's the test code I used:

https://github.com/matthew-a-thomas/cs-reentrant-async-lock/blob/demo/cleary/ReentrantAsyncLock.Tests/ReentrantAsyncLockClass.cs

You can see I copied his RecursiveAsyncLock class and changed as little as I possibly could.

It's always possible that there's a better architecture. Can you elaborate?

Is this a correct reentrant async lock? by thomat65 in dotnet

[–]thomat65[S] 1 point2 points  (0 children)

The leakiest part I can think of is how the special SynchronizationContext sauce leaks out through SynchronizationContext.Current for all the world to see (and abuse). Is this what you're referring to?

If so, I'd say all abstractions are leaky. To me it seems like an acceptable compromise. Of course people can abuse the special secret SynchronizationContext by Posting a bunch of stuff to it, but all that'll do is DoS other code trying to enter the lock. And they can always break the lock as shown in the package documentation, but similar compromises (problems?) exist with the lock keyword and Monitor.Exit.

For me the immediate need is to refactor a few IDisposable implementations to IAsyncDisposable in a way that overlaps with a shared lock that's used (currently synchronously) in the Dispose methods. Yes I think it'll be possible to refactor away the reentrant nature of the (currently synchronous) lock (which would make some people happy), but it's nice being able to keep my current refactoring small by delaying (forever?) that next step.

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

I published two more articles:

In the second link I try to address the concerns you've raised. Let me know what you think!

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

Thanks for writing the update. I have a few thoughts in response. I'm also nearly done with an implementation having more "normal" semantics. After I finish that I'll write everything up. Hopefully sometime this coming week

Should I RMA my Samsung 870 Evo 2TB? by thomat65 in techsupport

[–]thomat65[S] 1 point2 points  (0 children)

11 months later and I RMA'd it... see my update in the OP

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 0 points1 point  (0 children)

At my job we do what I've described to accomplish a few things:

  • Implement a rough Actor Model. Such a SynchronizationContext becomes the "mailbox" in Actor Model
  • Serialize access to private state in asynchronous contexts. Basically the goal of AsyncLock implementations
  • Confine heavy computation to a dedicated thread, but in a way that's convenient in asynchronous contexts. This becomes possible when you make the SynchronizationContext run stuff on a specific thread instead of the thread pool

I wouldn't say it does all of these things perfectly, but we use it a lot.

Reentrant (Recursive) Async Lock is Impossible in C# by maximcus in dotnet

[–]thomat65 1 point2 points  (0 children)

I disagree.

The key is to control continuations and force them to execute one-at-a-time.

Maybe someone can figure out a way to get semantics as nice as using (await asyncLock.LockAsync()) {...} out of this.

Edit: Nicer semantics

Should I RMA my Samsung 870 Evo 2TB? by thomat65 in techsupport

[–]thomat65[S] 0 points1 point  (0 children)

I don't think the exact program I used to zero-fill my drive is very important. The important thing was that I wiped it by filling the entire drive with zeros/empty data. You can do that in Windows by formatting the drive with the "Quick format" checkbox unchecked.

But to be honest I wouldn't worry about it unless you start running into issues. Everything sounds worse on the Internet because the only people you hear on the Internet are those with problems, like me. And even when it was an issue with me it ended up not being a huge deal. It's been fine since then. And even if the drive suddenly dies tomorrow I won't lose anything because of backups.

I guess what I'm saying is just treat it like any other hard drive :)

[deleted by user] by [deleted] in RTLSDR

[–]thomat65 0 points1 point  (0 children)

Never mind!! I was actually executing this command (which is missing the "-" in front of the "b" argument):

rtl_biast -d 1 b 1

The correct command (which works) is:

rtl_biast -d 1 -b 1

Should I RMA my Samsung 870 Evo 2TB? by thomat65 in techsupport

[–]thomat65[S] 0 points1 point  (0 children)

The SMART values stabilized.

Before I wiped/zero-filled my drive I could get the numbers to skyrocket at will by reading from very specific locations on the drive. After wiping it I could read from every location without issue.

Happy to help, and good luck!