Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Mr Shiny Pants posted:

Is it safe to use recursive functions within AspNetCore?

I've written a Websocket handler for Giraffe, which runs on Kestrel, and after banging my head against the desk on how it all fits together I finally stumbled upon the part that you need to block the WebSocket receiver within the pipeline.

I was under the impression that the pipeline would receive the socket and hand it off to some other function thereby ending the pipeline, but it seems it remains in flight for the duration of the connection.

Inside the middleware I've written a recursive function that handles the received message and returns another receiveasync effectively blocking that specific pipeline.

I've never worked with websockets, but:

https://docs.microsoft.com/it-it/aspnet/core/fundamentals/websockets?view=aspnetcore-3.0

quote:

When using a WebSocket, you must keep the middleware pipeline running for the duration of the connection. If you attempt to send or receive a WebSocket message after the middleware pipeline ends, you may get an exception like the following:

quote:

When accepting the WebSocket connection before beginning the loop, the middleware pipeline ends. Upon closing the socket, the pipeline unwinds. That is, the request stops moving forward in the pipeline when the WebSocket is accepted. When the loop is finished and the socket is closed, the request proceeds back up the pipeline.

That really sounds to me like you're supposed to lock down that pipeline as long as the websocket is active, and it makes intuitive sense as well - it's an ongoing connection, if you were dropping the pipeline and being stateless you'd just be doing HTTP.

NihilCredo fucked around with this message at 17:16 on Oct 3, 2019

Adbot
ADBOT LOVES YOU

Mr Shiny Pants
Nov 12, 2012

NihilCredo posted:

I've never worked with websockets, but:

https://docs.microsoft.com/it-it/aspnet/core/fundamentals/websockets?view=aspnetcore-3.0



That really sounds to me like you're supposed to lock down that pipeline as long as the websocket is active, and it makes intuitive sense as well - it's an ongoing connection, if you were dropping the pipeline and being stateless you'd just be doing HTTP.

Yes, that was also what I stumbled upon and how it works right now, I was just wondering if making it recursive within a framework would blow up. Tail call optimisation and the like.

Mr Shiny Pants fucked around with this message at 17:20 on Oct 3, 2019

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Mr Shiny Pants posted:

Yes, that was also what I stumbled upon and how it works right now, I was just wondering if making it recursive within a framework would blow up. Tail recursion and the like.

Ahh. I think tail call optimizations happen at a much lower level, so you should be able to just open up the .dll with an IL analyzer and see either (a) the loop version of the function (created by the compiler) or (b) the tail. IL command.

Or you can just run your loop a few dozen times in release mode, then log an exception and check the call stack size.

Incidentally, can you post your Giraffe pipeline? I have to deal with the lovely MS controller architecture because I need OpenAPI autogeneration, but I've always wanted to switch to Giraffe because that composition root looks gorgeous.

Mr Shiny Pants
Nov 12, 2012

NihilCredo posted:

Ahh. I think tail call optimizations happen at a much lower level, so you should be able to just open up the .dll with an IL analyzer and see either (a) the loop version of the function (created by the compiler) or (b) the tail. IL command.

Or you can just run your loop a few dozen times in release mode, then log an exception and check the call stack size.

Incidentally, can you post your Giraffe pipeline? I have to deal with the lovely MS controller architecture because I need OpenAPI autogeneration, but I've always wanted to switch to Giraffe because that composition root looks gorgeous.

Hmmm that's quite the thing..... I'll try and figure out how that's working. Thanks.

Giraffe is really nice for normal HTTP stuff, the websocket logic does not really work with the >=> ( kleisli?) combinators. Documentation is a bit sparse, so maybe I am just not understanding it correctly.

I have it at work, I'll post it when I am back at my desk.

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

NihilCredo posted:

Ahh. I think tail call optimizations happen at a much lower level, so you should be able to just open up the .dll with an IL analyzer and see either (a) the loop version of the function (created by the compiler) or (b) the tail. IL command.

Or you can just run your loop a few dozen times in release mode, then log an exception and check the call stack size.

Incidentally, can you post your Giraffe pipeline? I have to deal with the lovely MS controller architecture because I need OpenAPI autogeneration, but I've always wanted to switch to Giraffe because that composition root looks gorgeous.

Correct me if I'm wrong, but for some esoteric reason C# does not do tail call recursion. Any recursive function can overflow.

Cold on a Cob
Feb 6, 2006

i've seen so much, i'm going blind
and i'm brain dead virtually

College Slice

Cuntpunch posted:

Correct me if I'm wrong, but for some esoteric reason C# does not do tail call recursion. Any recursive function can overflow.

Correct. Further discussion here: https://github.com/dotnet/csharplang/issues/2544

ulmont
Sep 15, 2010

IF I EVER MISS VOTING IN AN ELECTION (EVEN AMERICAN IDOL) ,OR HAVE UNPAID PARKING TICKETS, PLEASE TAKE AWAY MY FRANCHISE

Cuntpunch posted:

Correct me if I'm wrong, but for some esoteric reason C# does not do tail call recursion.

quote:

Thanks for the suggestion. We've considered emiting tail call instructions at a number of points in the development of the C# compiler. However, there are some subtle issues which have pushed us to avoid this so far:
1) There is actually a non-trivial overhead cost to using the .tail instruction in the CLR (it is not just a jump instruction as tail calls ultimately become in many less strict environments such as functional language runtime environments where tail calls are heavily optimized).
2) There are few real C# methods where it would be legal to emit tail calls (other languages encourage coding patterns which have more tail recursion, and many that rely heavily on tail call optimization actually do global re-writing (such as Continuation Passing transformations) to increase the amount of tail recursion).
3) Partly because of 2), cases where C# methods stack overflow due to deep recursion that should have succeeded are fairly rare.

All that said, we continue to look at this, and we may in a future release of the compiler find some patterns where it makes sense to emit .tail instructions.

Thanks,
Luke Hoban
Visual C# Compiler Program Manager

That was 2007, but it still seems to be the case:
https://github.com/dotnet/csharplang/issues/2544

e;f,b.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Cuntpunch posted:

Correct me if I'm wrong, but for some esoteric reason C# does not do tail call recursion. Any recursive function can overflow.

He's writing F#.

CapnAndy
Feb 27, 2004

Some teeth long for ripping, gleaming wet from black dog gums. So you keep your eyes closed at the end. You don't want to see such a mouth up close. before the bite, before its oblivion in the goring of your soft parts, the speckled lips will curl back in a whinny of excitement. You just know it.
Did Office 2019 Excel files get a new OLEDB connection string, by some esoteric chance? I've got perfectly good .xlsx files that aren't corrupted or anything -- they'll open in Excel -- but both Microsoft.ACE.OLEDB.12.0 and Microsoft.Jet.OLEDB.4.0 (I tried it just in case) are giving me the same Not In Expected Format errors. The same code will process other Excel files just fine and the department generating the ones that are failing have no idea what version of Excel they use or what could possibly be different about them, and I can't find any other connection strings on Google, but I'm utterly out of ideas and Not In Expected Format gives me dick-all to go off of.

mystes
May 31, 2006

CapnAndy posted:

Did Office 2019 Excel files get a new OLEDB connection string, by some esoteric chance? I've got perfectly good .xlsx files that aren't corrupted or anything -- they'll open in Excel -- but both Microsoft.ACE.OLEDB.12.0 and Microsoft.Jet.OLEDB.4.0 (I tried it just in case) are giving me the same Not In Expected Format errors. The same code will process other Excel files just fine and the department generating the ones that are failing have no idea what version of Excel they use or what could possibly be different about them, and I can't find any other connection strings on Google, but I'm utterly out of ideas and Not In Expected Format gives me dick-all to go off of.
Is it possible that you're running 64-bit office now and that's causing some sort of problem? I'm not sure if it actually makes a difference, but I think 64-bit just became the default as of Office 2019.

mystes fucked around with this message at 01:35 on Oct 4, 2019

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Does anybody know of a way to capture warning and errors from the output of dotnet build, other than manual regexes?

(Use case: in our CI system, I want to allow builds with warnings to be deployed to test, but prevent them from being deployed to production. I could just re-build with warnings-as-errors, but it would double CI times.)

EDIT: Found it, dotnet build can't do it but dotnet msbuild can use all the fancy stuff:

code:
dotnet msbuild '-fileloggerparameters:warningsonly;logfile=warnings.txt'

NihilCredo fucked around with this message at 21:16 on Oct 4, 2019

Mr Shiny Pants
Nov 12, 2012
I've scratched the websocket stuff, If you are still interested I can post the code.

I was working on something distributed that needs to keep all connected devices updated in semi realtime, after going through all the stuff needed for this, without going all in on SignalR, I've decided that it may be smarter to use Kafka and have all the clients connected to a specific topic over websockets.

Especially because it keeps track of where everyone is in reading the messages.

Portland Sucks
Dec 21, 2004
༼ つ ◕_◕ ༽つ
I'm working on some Azure data related tasks and struggling to do something that I think should be a pretty standard operation. I need to be able to open up a blob from a blob triggered function as a stream that's going to be a large CSV file and chunk it into some arbitrary number of gziped files between 10-100mb each (compressed).

I understand how to open up a blob and stream the data to another blob as a single compressed file but I'm getting kind of lost figuring out how to #1 create gzip files that fall within a designated size limit after compression and #2 how to break up the stream into multiple smaller files.

For reference I'm using the basic Azure function template that provides the source blob as a Stream and I'm attempting to use GZipStream with a block blob passed in as the reference. Can a single block blob be multiple files?

Jethro
Jun 1, 2000

I was raised on the dairy, Bitch!
I have no relevant experience, but I think the only way to know how large a compressed stream of data will be is to run the compression algorithm. Unless your source CSV is particularly pathological, your compression ratio is (I'm guessing) going to be around 5-10%, so maybe just chunk 500MB of uncompressed data at a time, and the compressed files will usually be 25-50MB. And then I think you're done, because it looks like it's relatively straightforward to take a bunch of smaller .gz files and get uncompressed output that is the same as concatenating the original uncompressed output.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I'm experimenting with migrating some code using a lot of IEnumerables as coroutines to using async-await. I have stubbed my toe in a few places:
1. I don't have the rig in front of me but I want to say it's running VS2018, and it really sucks at taking me to the site of an exception when something screws up in a Task. Is this something that has improved, or is there some manner in which I can improve getting the right call stack? I seem to get stranded in mscorlib with the exception message and I just kind of have to infer. This has be far been the most frustrating part of the whole thing.
2. What are people's thoughts on using var for automatic static type deduction when adapting code like this? I had a really nasty problem where I had some code waiting for a Task<object> and wound up somehow getting a Task<Task<object>>. I'm not sure if I can really tighten up the generic parameter any further since it's a wrapper for basically any kind of embedded call with any kind of return signature. I don't think typing it explicitly as Task<object> would have worked because Task<object> is also an object and just fine. But I'd love to get slapped on the wrist more for this at compile-time.
3. Has anybody had any luck trying to write a little scheduler for managing various things that may or may not block? I have one with a loop but it really was spinning on itself. With IEnumerables that didn't actually block in unit tests, I could assert that it should only loop once. With async-await, I was seeing over 2500 iterations for the same cases. I'd like to use some kind of tick count as a canary for particularly-bad coroutines, but not if I can normally expect a lot of looping around like this.

Update: It looks like I was actually using VS2017. I'm messing around with VS2019 and getting different effects in the debugger. Currently, if I debug a unit test, it'll take me to the right spot, but I have to try some more to ensure I'm getting into exposure into Tasks that are acting up.

Rocko Bonaparte fucked around with this message at 05:53 on Oct 11, 2019

raminasi
Jan 25, 2005

a last drink with no ice

Rocko Bonaparte posted:

I'm experimenting with migrating some code using a lot of IEnumerables as coroutines to using async-await. I have stubbed my toe in a few places:
1. I don't have the rig in front of me but I want to say it's running VS2018, and it really sucks at taking me to the site of an exception when something screws up in a Task. Is this something that has improved, or is there some manner in which I can improve getting the right call stack? I seem to get stranded in mscorlib with the exception message and I just kind of have to infer. This has be far been the most frustrating part of the whole thing.
2. What are people's thoughts on using var for automatic static type deduction when adapting code like this? I had a really nasty problem where I had some code waiting for a Task<object> and wound up somehow getting a Task<Task<object>>. I'm not sure if I can really tighten up the generic parameter any further since it's a wrapper for basically any kind of embedded call with any kind of return signature. I don't think typing it explicitly as Task<object> would have worked because Task<object> is also an object and just fine. But I'd love to get slapped on the wrist more for this at compile-time.
3. Has anybody had any luck trying to write a little scheduler for managing various things that may or may not block? I have one with a loop but it really was spinning on itself. With IEnumerables that didn't actually block in unit tests, I could assert that it should only loop once. With async-await, I was seeing over 2500 iterations for the same cases. I'd like to use some kind of tick count as a canary for particularly-bad coroutines, but not if I can normally expect a lot of looping around like this.

Update: It looks like I was actually using VS2017. I'm messing around with VS2019 and getting different effects in the debugger. Currently, if I debug a unit test, it'll take me to the right spot, but I have to try some more to ensure I'm getting into exposure into Tasks that are acting up.

A Task<Task<object>> is not actually a Task<object> - by all means stop using var if it will help avoid bugs.

rarbatrol
Apr 17, 2011

Hurt//maim//kill.
I've seen async/await focused Roslyn code analyzers out there that will yell at you if you downcast Task<T> to Task, Task, to void, and other stuff like that.

raminasi
Jan 25, 2005

a last drink with no ice

rarbatrol posted:

I've seen async/await focused Roslyn code analyzers out there that will yell at you if you downcast Task<T> to Task, Task, to void, and other stuff like that.

Some of them also do stupid poo poo like demand that you remove "unnecessary" async despite the semantics of
C# code:
public async Task<Butt> GetButt()
{
    return await GetButtInternal();
}
being different from
C# code:
public Task<Butt> GetButt()
{
    return GetButtInternal();
}
:argh:

Another stupid one is demanding ConfigureAwait on all awaits even if the thing being awaited doesn't actually have a ConfigureAwait method.

Xik
Mar 10, 2011

Dinosaur Gum

raminasi posted:

Some of them also do stupid poo poo like demand that you remove "unnecessary" async despite the semantics of
C# code:
public async Task<Butt> GetButt()
{
    return await GetButtInternal();
}
being different from
C# code:
public Task<Butt> GetButt()
{
    return GetButtInternal();
}
:argh:


I think the idea of that suggestion is that you should really make sure that's what you want to be doing. Like, is there a reason you're using await, maybe just return the task so the caller can decide.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I was able to convert all those IEnumerable coroutines I had written to use async-await. It got rid of a boilerplate I had to use all over the place, but the current code is powered by voodoo. I had tried to set this up so that my coroutines were managed by its own scheduler so I can throttle them back and I have ... mostly succeeded. I think I have completely succeeded but I'm still trying to figure out why the scheduler ends up ticking through the scheduler loop more than once. However, it not longer ticks through the scheduler 2,000+ times. It's usually just twice, but I was trying to make it just once--and consistently once--for the sake of asserting good behavior.

Anyways, what I really don't understand is why I might deadlock if I have a sequence like this:

code:
awaiting on an async Task Call() that is...
awaiting on an async Task Call() that is...
awaiting on a custom awaiter that sets up the continuation to resume everything on the next tick
Okay I actually kind of see the problem there in that the scheduler can't move on because it's waiting through a few layers of Tasks that are just stopped. But what I don't get is how removing the await on the middle of that sandwich unjammed everything. So...
code:
awaiting on an async Task Call() that is...
NO LONGER AWAITING--but just kind of calling without returning anything an async Task Call() that is...
awaiting on a custom awaiter that sets up the continuation to resume everything on the next tick
Basically, I deleted "await" from the middle section and it went on like I wanted it to.

Xik
Mar 10, 2011

Dinosaur Gum
I'm struggling to interpret what you're doing but removing the await will just mean that you continue execution in the current context while the task is performing work.

If you never await it then you can't guarantee the work will be done when you want it to. I assume you're not using the return value immediately otherwise you'll see a bunch of type errors as the result will be a Task instead of your type.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

Xik posted:

I'm struggling to interpret what you're doing but removing the await will just mean that you continue execution in the current context while the task is performing work.

If you never await it then you can't guarantee the work will be done when you want it to. I assume you're not using the return value immediately otherwise you'll see a bunch of type errors as the result will be a Task instead of your type.

I can try to show more but at the some point, the context gets overwhelming. I'm trying to write an embeddable interpreter using something like a Python 3 syntax. I intend to have it run a pile of scripts as their own coroutines and am trying to use async-await to make it work.

The first await is the scheduler awaiting for the currently-scheduled coroutine--as a Task--to unblock.
The second level of await is a wrapper to turn everything the scheduler sees into what looks like continuations. Custom awaiters will inform the scheduler they are blocking and file the continuation it gets from OnCompleted with the scheduler using this general mechanism. However, to make things more uniform, new coroutines are launched in the same way--except that the continuation in their case is the initial start of their code.
The third level of the await is the interpreter itself running into a custom WAIT opcode I created to have the current script yield for a tick. It is awaiting on a custom awaiter that files the continuation with the scheduler that'll just line it up to run on the next tick.

When run that way, it deadlocks. However, if I remove the await from the second level, it doesn't. It hurts my head. All unit tests are passing this way. Most are just running in one tick, but I threw in that WAIT opcode particularly to make sure I can create some blocking calls that will stop interpreting on one script while having the scheduler just move on. This all seems to work with that. In fact, I also fixed the issue with multiple ticks tonight so it's pretty much working like I would have wanted it to. I just can't explain to myself how I stripped away that await keyword to do it.

Jethro
Jun 1, 2000

I was raised on the dairy, Bitch!
I don't have any experience with async-await (though I read a whole bunch of Fabulous Adventures in Coding shortly before the feature was added to C#), but it seems to me that if you fix things by causing the second level of task to just fire off into the ether instead of awaiting it, there's a problem with the thread continuing when the task completes. Did you test what happens if you do
code:
awaiting on an async Task Call() that is...
awaiting on an async Task Call() that is...
just firing off into the void a custom awaiter that sets up the continuation to resume everything on the next tick
If that also fixes things, then it's probably something with the bottom level custom awaiter. If you tested and it's only the second level that has the problem, then presumably you did something weird in your second level custom awaiter.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

Jethro posted:

I don't have any experience with async-await (though I read a whole bunch of Fabulous Adventures in Coding shortly before the feature was added to C#), but it seems to me that if you fix things by causing the second level of task to just fire off into the ether instead of awaiting it, there's a problem with the thread continuing when the task completes. Did you test what happens if you do
code:
awaiting on an async Task Call() that is...

awaiting on an async Task Call() that is...
just firing off into the void a custom awaiter that sets up the continuation to resume everything on the next tick
If that also fixes things, then it's probably something with the bottom level custom awaiter. If you tested and it's only the second level that has the problem, then presumably you did something weird in your second level custom awaiter.

I hadn't tested it since there is a very specific pattern I'm requiring at the bottom level and it's not that. But I see your point and I might force it for the sake of shenanigans. That specific pattern could have a flaw. I also agree with your idea that without the await on the second level, I should be just flinging that code out into the void... yet that doesn't appear to happen. What seems to happen is:

The scheduler continues to block until the WAIT is processed
It finishes its tick loop
the continuation of the script is put into the active queue (since WAIT blocks 1 tick)
In the second tick loop, it finishes it. Two ticks--as expected in this particular scenario; all other scenarios tick just once since they don't block midway.

For more details on the mess, imagine I'm creating a game script to run some dialogs:
code:
announce("Hey, this should come up in a box with some text and block until the user hits the action button")
The whole point of the system is to be able to block that script without blocking the thread so that the engine can actually process everything to display the text and accept the input across what could be multiple ticks of the interpreter's scheduler. So roughly what's going on is that:

(this is all on GitHub so I get to just link to it all!)

1. The first await is the scheduler letting the script run synchronously.
2. The second level is a wrapper to run the script as a general continuation. This is because .NET's INotifyCompletion's OnCompleted() gives me a continuation to resume and I didn't want to manage two code paths. So I treat starting a new script as if I'm resuming it from it's main entry point. This continuation originally awaited on the script but doesn't any more.
3. The third level await in this case would be on a external function call that processes announce(). What I link here is the WAIT opcode I'm using in my current test, though. Since it wouldn't be finished in this tick, the internals will implement a customer awaiter (YieldTick version used in WAIT scenario here) so it can harvest the continuation .NET gives to OnCompleted(). This is shuffled back to the scheduler and put in a blocked queue. When the command does complete, the scheduler gets notified to run it again, and it will resume the continuation on the next tick.

I am proceeding to implement a mock test for this scenario. I scratched something up to do this exact thing without the interpreter in between and it did work then. Adding in the actual interpreter and its scheduling of course adds in many more moving parts, and one of them is that second-level wrapper. So I suppose I'll have a real test seeing how that behaves and maybe that'll explain it.

PS Sorry if this is crazy dense. This has been some brain-bendy poo poo and I didn't get this far without prototyping a simpler scenario first. Even that was rough on my brain. This is me messing around with toy projects after 11PM and the other side of my brain takes over and Mr. Hyde comes out.

Chrungka
Jan 27, 2015

raminasi posted:

Some of them also do stupid poo poo like demand that you remove "unnecessary" async despite the semantics of
C# code:
public async Task<Butt> GetButt()
{
    return await GetButtInternal();
}
being different from
C# code:
public Task<Butt> GetButt()
{
    return GetButtInternal();
}
:argh:

Another stupid one is demanding ConfigureAwait on all awaits even if the thing being awaited doesn't actually have a ConfigureAwait method.

It actually makes quite a difference. async keyword instructs compiler to build a state machine to process continuation of the awaited task. Consider this simple example:

C# code:
public static Task Inner() => Task.CompletedTask;
public static Task Outer() => Inner();
public static async Task AsyncOuter() => await Inner();
For simple Outer() compiler generates straightforward IL code like this:
code:
.method public hidebysig static class [netstandard]System.Threading.Tasks.Task 
        Outer() cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  call       class [netstandard]System.Threading.Tasks.Task ClassLibrary1.Class1::Inner()
  IL_0005:  ret
} // end of method Class1::Outer
IL code of async/await code requires much more instructions. What follows is just the AsyncOuter() method. I will leave out whole hidden <AsyncOuter>d__2 class that implements the async state machine.
code:
.method public hidebysig static class [netstandard]System.Threading.Tasks.Task 
        AsyncOuter() cil managed
{
  .custom instance void [netstandard]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [netstandard]System.Type) = ( 01 00 25 43 6C 61 73 73 4C 69 62 72 61 72 79 31   // ..%ClassLibrary1
                                                                                                                                           2E 43 6C 61 73 73 31 2B 3C 41 73 79 6E 63 4F 75   // .Class1+<AsyncOu
                                                                                                                                           74 65 72 3E 64 5F 5F 32 00 00 )                   // ter>d__2..
  .custom instance void [netstandard]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       52 (0x34)
  .maxstack  2
  .locals init (class ClassLibrary1.Class1/'<AsyncOuter>d__2' V_0,
           valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1)
  IL_0000:  newobj     instance void ClassLibrary1.Class1/'<AsyncOuter>d__2'::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create()
  IL_000c:  stfld      valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder ClassLibrary1.Class1/'<AsyncOuter>d__2'::'<>t__builder'
  IL_0011:  ldloc.0
  IL_0012:  ldc.i4.m1
  IL_0013:  stfld      int32 ClassLibrary1.Class1/'<AsyncOuter>d__2'::'<>1__state'
  IL_0018:  ldloc.0
  IL_0019:  ldfld      valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder ClassLibrary1.Class1/'<AsyncOuter>d__2'::'<>t__builder'
  IL_001e:  stloc.1
  IL_001f:  ldloca.s   V_1
  IL_0021:  ldloca.s   V_0
  IL_0023:  call       instance void [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start<class ClassLibrary1.Class1/'<AsyncOuter>d__2'>(!!0&)
  IL_0028:  ldloc.0
  IL_0029:  ldflda     valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder ClassLibrary1.Class1/'<AsyncOuter>d__2'::'<>t__builder'
  IL_002e:  call       instance class [netstandard]System.Threading.Tasks.Task [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task()
  IL_0033:  ret
} // end of method Class1::AsyncOuter
In the end, the suggestion instructs you to write more efficient code. If you want to know more, just open up a VS command prompt and ildasm your assembly.

raminasi
Jan 25, 2005

a last drink with no ice

Xik posted:

I think the idea of that suggestion is that you should really make sure that's what you want to be doing. Like, is there a reason you're using await, maybe just return the task so the caller can decide.

We bring it in as a StyleCop rule that isn't a "suggestion." It breaks the build unless you go out of your way to disable it. (This might be a stupid Rider thing, I don't know.) And the caller can't decide anything in this case - all it sees is a Task.

Chrungka posted:

It actually makes quite a difference. async keyword instructs compiler to build a state machine to process continuation of the awaited task. Consider this simple example:

C# code:
public static Task Inner() => Task.CompletedTask;
public static Task Outer() => Inner();
public static async Task AsyncOuter() => await Inner();
For simple Outer() compiler generates straightforward IL code like this:
code:
.method public hidebysig static class [netstandard]System.Threading.Tasks.Task 
        Outer() cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  call       class [netstandard]System.Threading.Tasks.Task ClassLibrary1.Class1::Inner()
  IL_0005:  ret
} // end of method Class1::Outer
IL code of async/await code requires much more instructions. What follows is just the AsyncOuter() method. I will leave out whole hidden <AsyncOuter>d__2 class that implements the async state machine.
code:
.method public hidebysig static class [netstandard]System.Threading.Tasks.Task 
        AsyncOuter() cil managed
{
  .custom instance void [netstandard]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [netstandard]System.Type) = ( 01 00 25 43 6C 61 73 73 4C 69 62 72 61 72 79 31   // ..%ClassLibrary1
                                                                                                                                           2E 43 6C 61 73 73 31 2B 3C 41 73 79 6E 63 4F 75   // .Class1+<AsyncOu
                                                                                                                                           74 65 72 3E 64 5F 5F 32 00 00 )                   // ter>d__2..
  .custom instance void [netstandard]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       52 (0x34)
  .maxstack  2
  .locals init (class ClassLibrary1.Class1/'<AsyncOuter>d__2' V_0,
           valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder V_1)
  IL_0000:  newobj     instance void ClassLibrary1.Class1/'<AsyncOuter>d__2'::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Create()
  IL_000c:  stfld      valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder ClassLibrary1.Class1/'<AsyncOuter>d__2'::'<>t__builder'
  IL_0011:  ldloc.0
  IL_0012:  ldc.i4.m1
  IL_0013:  stfld      int32 ClassLibrary1.Class1/'<AsyncOuter>d__2'::'<>1__state'
  IL_0018:  ldloc.0
  IL_0019:  ldfld      valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder ClassLibrary1.Class1/'<AsyncOuter>d__2'::'<>t__builder'
  IL_001e:  stloc.1
  IL_001f:  ldloca.s   V_1
  IL_0021:  ldloca.s   V_0
  IL_0023:  call       instance void [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::Start<class ClassLibrary1.Class1/'<AsyncOuter>d__2'>(!!0&)
  IL_0028:  ldloc.0
  IL_0029:  ldflda     valuetype [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder ClassLibrary1.Class1/'<AsyncOuter>d__2'::'<>t__builder'
  IL_002e:  call       instance class [netstandard]System.Threading.Tasks.Task [netstandard]System.Runtime.CompilerServices.AsyncTaskMethodBuilder::get_Task()
  IL_0033:  ret
} // end of method Class1::AsyncOuter
In the end, the suggestion instructs you to write more efficient code. If you want to know more, just open up a VS command prompt and ildasm your assembly.

It also makes "quite a difference" in that the semantics are different, which is the thing I was posting about but I guess you ignored because you wanted to show off that you knew how to disassemble things.

EssOEss
Oct 23, 2006
128-bit approved
/simpsons voice/ Ha-ha!

Whenever I think of giving a try to static analysis, I get put off by "rules" that are more like "Are you sure?" suggestions and I have always had to go through some extra effort because yeah, actually I am sure. The only time static analysis ever proved useful to me was when I accidentally captured the foreach variable way back when they were not per-iteration but per-foreach scoped. What kind of errors do you guys actually catch with static analysis?

On another topic, is there some good "practical C#" learning book for beginners? I am training some newbies and all books I can find are the most boring drivel that just goes through language features one by one, including all the bullshit language features nobody should use in 2019. I can just see the book authors taking the C# language docs TOC and just slamming chapters into their book for each TOC entry just to pad their page count. Is there a beginner book that focuses on actually achieving things with code, not just mindless language exercises?

Mr Shiny Pants
Nov 12, 2012
Anyone used OpenTK? I have some problems with audio recording, namely getting stuff from the OpenAL buffers into something like a memory stream. There are a lot of moving parts in the library and I don't really know how to glue them all together.

redleader
Aug 18, 2005

Engage according to operational parameters
hangfire allows you to create queues, allowing you to process different background jobs on different hangfire servers

hangfire makes it sound like these queues work properly and predictably and as you'd expect

hangfire loving lies

zerofunk
Apr 24, 2004

redleader posted:

hangfire allows you to create queues, allowing you to process different background jobs on different hangfire servers

hangfire makes it sound like these queues work properly and predictably and as you'd expect

hangfire loving lies

I haven't looked at in a while, but from what I remember, somewhere in the documentation it gives the caveat that all your background tasks must be idempotent because they could be interrupted at any time and then it'll retry them. It seemed like that warning should have been a lot more prominent though as it is somewhat at odds with the marketing message of easily backgrounding all your tasks.

ljw1004
Jan 18, 2005

rum

EssOEss posted:

What kind of errors do you guys actually catch with static analysis?

The Roslyn team used static analysis to ensure that when your method accepts a CancellationToken but you fail to pass it on to a Cancellation-token-taking method that your invoking, then it's likely an oversight and should be fixed. It's an issue because so many .NET framework methods have overloads which allow you to omit the cancellation token.

(also for perf things, since they needed their inner-loops not to allocate gratuitously e.g. $"hello {i}" needlessly boxes the int while $"hello {i.ToString()}" doesn't, but that's pretty niche).

B-Nasty
May 25, 2005

zerofunk posted:

caveat that all your background tasks must be idempotent because they could be interrupted at any time and then it'll retry them

LOL.

Don't use a baby's-first-queue like Hangfire; just invest the time to layer in Azure Service Bus or RabbitMQ. These give you infinitely more options regarding how queue messages are completed/aborted/retried, and don't tie you tightly to a particular implementation (assuming you abstract away the queue message receiver and queue message specifics.)

EssOEss
Oct 23, 2006
128-bit approved

ljw1004 posted:

The Roslyn team used static analysis to ensure that when your method accepts a CancellationToken but you fail to pass it on to a Cancellation-token-taking method that your invoking, then it's likely an oversight and should be fixed.

Oh, that's a nice one! Is this one of the default rules or available on NuGet?

--

On another topic, is there some obvious direction to go if I want a minimum effort CRUD website against some data source (e.g. SQL)? I just want something quick and dirty that throws up a database table as a data grid and lets users mess with it as if they were using Excel (in fact, this is a workflow that's done in Excel today). This data is used as input to drive some automation processes. All my work so far has been on products with more involved requirements than "just let users do whatever with the raw data", so I am not sure what fills this niche.

My fallback option is just to stick the input data into some JSON/XML file in a Git repository and tell users to mess with that but I feel like this might be too big of a detraction from Excel for some users.

raminasi
Jan 25, 2005

a last drink with no ice
When I try dotnet build I get this:
code:
C:\Program Files\dotnet\sdk\3.0.100\Sdks\Microsoft.NET.Sdk.WindowsDesktop\targets\Microsoft.WinFX.targets(243,9): error MC1000: Unknown build error, 'Could not find assembly 'System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'.
That's an old-rear end version of System.Core, and I have no idea why it's looking for it. (The .targets file is no help.) I can build from within Visual Studio fine. Any ideas?

e: Command-line msbuild works.

edit again: I'm trying to use the new Microsoft.NET.Sdk.WindowsDesktop in .NET Core 3, and apparently if I actually have a WPF control or window in my project this happens. Hooray!

raminasi fucked around with this message at 23:17 on Oct 19, 2019

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



EssOEss posted:

On another topic, is there some obvious direction to go if I want a minimum effort CRUD website against some data source (e.g. SQL)? I just want something quick and dirty that throws up a database table as a data grid and lets users mess with it as if they were using Excel (in fact, this is a workflow that's done in Excel today). This data is used as input to drive some automation processes. All my work so far has been on products with more involved requirements than "just let users do whatever with the raw data", so I am not sure what fills this niche.

My fallback option is just to stick the input data into some JSON/XML file in a Git repository and tell users to mess with that but I feel like this might be too big of a detraction from Excel for some users.

FWIW, you can pull data right out of a DB with Excel using any extant view or a custom query. Not sure if you can persist back into the DB with Excel, though.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

EssOEss posted:

On another topic, is there some obvious direction to go if I want a minimum effort CRUD website against some data source (e.g. SQL)? I just want something quick and dirty that throws up a database table as a data grid and lets users mess with it as if they were using Excel (in fact, this is a workflow that's done in Excel today). This data is used as input to drive some automation processes. All my work so far has been on products with more involved requirements than "just let users do whatever with the raw data", so I am not sure what fills this niche.

My fallback option is just to stick the input data into some JSON/XML file in a Git repository and tell users to mess with that but I feel like this might be too big of a detraction from Excel for some users.

If it's literally SQL and not a generic data source, https://www.adminer.org/en/editor/#download probably suits your needs.

Demo: https://demo.adminer.org/editor.php?username=

Boz0r
Sep 7, 2006
The Rocketship in action.
Is it possible to prevent reflection and serialization in singletons in C#?

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
What is your goal here?

Are you trying to prevent yourself from accidentally breaking the singleton contract by using reflection or serialisation internally?

Are you trying to prevent malicious code from deliberately creating multiple singletons for some nefarious purpose?

Are you trying to make it easier for benign-yet-incompetent developers to use your singleton the "right way" as opposed to hacking up a seemingly-working-until-it-doesn't solution using reflection?

Boz0r
Sep 7, 2006
The Rocketship in action.

Jabor posted:

What is your goal here?

Are you trying to prevent yourself from accidentally breaking the singleton contract by using reflection or serialisation internally?

Are you trying to prevent malicious code from deliberately creating multiple singletons for some nefarious purpose?

Are you trying to make it easier for benign-yet-incompetent developers to use your singleton the "right way" as opposed to hacking up a seemingly-working-until-it-doesn't solution using reflection?

The second and third one, but mostly theoretical. I know that in Java you can use an enum to prevent both of these things, but C# doesn't support that.

Adbot
ADBOT LOVES YOU

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
You can put your singleton in an assembly with the DisablePrivateReflection attribute.

Keep in mind that, like the Java solution, this doesn't stop anyone copying your code and changing it to allow what they want to do.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply