Pascal feature proposal: begin(syncObj) lazarus / 64-FPC development ---------------------------------------------------------------------- Cooler · 2015-07-26 07:42 When you develop a thread-safe code you probably use critical sections (or other sync objects) very often. So there are TONS of constructions like: Code: [Select]  EnterCriticalSection(cSect);   try    ...   finally    LeaveCriticalSection(cSect);   end;  Besides this makes code bulky, it enforces programmer to carefully track where the protection is really needed and where it is redundant. Wrong decision here causes performance degradation (best case) or sync issues like deadlocks (in worst case). So my proposal has 2 goals: - make code cleaner and more readable; - free programmer from manual optimization (that can lead to severe bugs), which can be done by compiler. I propose to extend the "begin ... end" brackets construction syntax like this: Code: [Select] begin(cSect)   ...  end; which is equivalent to the code example above, where cSect may be an RTL critical section or RTL sync object with Lock()/Unlock() or Enter()/Leave() methods. The compiler can then optimize this construction and omit synchronization if: - the code section doesn't reference any non-local variables (unnecessary call), - the code section is always executed from places already protected by the same sync object (redundant call). In addition, it can also make 2 routine instances (protected and unprotected) to use each version from appropriate places. So this syntax makes it much easier to make code thread-safe without headache about overheads and making the code bulky and unreadable. ---------------------------------------------------------------------- marcov · 2015-07-26 08:25 Have you looked at http://www.freepascal.org/faq.var#extensionselect   ? Shorthand notations are generally frowned upon as motivation for language. (unless it is REALLY,REALLY common) However I have tons of (250kloc) threadsafe code, and maybe have 20 of such constructs because they are generally deep in the business objects, and the construct is autoexpanded by the IDE, so typing is not even the limit. ---------------------------------------------------------------------- Cooler · 2015-07-26 20:59 Quote from: marcov on July 26, 2015, 10:25:56 amHave you looked at http://www.freepascal.org/faq.var#extensionselect   ? No, thanks for the info. QuoteShorthand notations are generally frowned upon as motivation for language. (unless it is REALLY,REALLY common) It's not just shorthand, it's about doing less code maintenance work. It's like using dynamic arrays for data instead of manual heap allocation and bothering about memory release - easier to write, easier to read/understand, less chances to make a bug => makes language more attractive and competitive. Anyway, there are many shorthand notations already supported in the language (with, for..in, automatic reference counting, string operators, implicit string conversions and so on), so it looks a bit strange. The language is evolving. BTW, Objective-C has similar @synchronized directive. Quoteand the construct is autoexpanded by the IDE, so typing is not even the limit.Could you please explain this? ---------------------------------------------------------------------- BeniBela · 2015-07-26 21:55 That is why I always say  block scoped variables, referenced counted classes and automatic type inference are mandatory features for a language. Then you could write Code: [Select]begin   var guard := synchronizedSection(cSect); end; But for .. in is a tiny step in the right direction. You can write Code: [Select]var temp: boolean; //begin   for temp in synchronizedSection(cSect) do begin       end; with something like Code: [Select]type   TSectionGuardEnumerator = class   private     FCurrent: boolean;     section: PRTLCriticalSection;   public     function MoveNext: Boolean;     property Current: boolean read FCurrent;     function GetEnumerator: TSectionGuardEnumerator;     destructor destroy(); override;   end;   function synchronizedSection(var section: TRTLCriticalSection): TSectionGuardEnumerator;   begin     result := TSectionGuardEnumerator.Create;     result.section:=@section;     result.FCurrent := true;     EnterCriticalsection(section);   end;   destructor TSectionGuardEnumerator.destroy;   begin     LeaveCriticalsection(section^);   end;   function TSectionGuardEnumerator.MoveNext: Boolean;   begin     result := Current;     FCurrent := false;   end;   function TSectionGuardEnumerator.GetEnumerator: TSectionGuardEnumerator;   begin     result := self;   end; ---------------------------------------------------------------------- Basile B. · 2015-07-26 23:52 Quote from: Cooler on July 26, 2015, 09:42:27 amWhen you develop a thread-safe code you probably use critical sections (or other sync objects) very often. So there are TONS of constructions like: Code: [Select]  EnterCriticalSection(cSect);   try    ...   finally    LeaveCriticalSection(cSect);   end;  Besides this makes code bulky, it enforces programmer to carefully track where the protection is really needed and where it is redundant. Wrong decision here causes performance degradation (best case) or sync issues like deadlocks (in worst case). So my proposal has 2 goals: - make code cleaner and more readable; - free programmer from manual optimization (that can lead to severe bugs), which can be done by compiler. I propose to extend the "begin ... end" brackets construction syntax like this: Code: [Select] begin(cSect)   ...  end; To make it more versatile (not only dedicated to TCriticalSection, which the compiler doesn't know BTW, it's a library type... ), I'd see more this as an operator that could be overloaded for a particular type, for example: Code: [Select]operator begin(Value: TCriticalSection); begin   Value.Enter; end; operator end(Value: TCriticalSection); begin   Value.Release; end; Then the compiler could look if the operator overload exists for the variable of the type found within, let's call it, a BeginExpression Another possible usage: Code: [Select]operator begin(var Value: TMemoryStream); begin   Value := TMemoryStream.Create; end; operator end(var Value: TMemoryStream); begin   Value.Free; end; The problem is that currently begin is not an expression. It's a terminal token. ---------------------------------------------------------------------- Cooler · 2015-07-27 11:34 Quote from: BBasile on July 27, 2015, 01:52:32 amTo make it more versatile (not only dedicated to TCriticalSection, which the compiler doesn't know BTW, it's a library type... ), I'd see more this as an operator that could be overloaded for a particular type, for example: Without try/finally it's pretty useless (unless end operator is always executed). Also such generic approach doesn't allow optimization: compiler will always generate code even if it's redundant. ---------------------------------------------------------------------- Basile B. · 2015-07-27 12:46 Quote from: Cooler on July 27, 2015, 01:34:25 pmWithout try/finally it's pretty useless (unless end operator is always executed). In my idea it has to be always executed. They go in pair. Also at compile time: an error if operator begin is defined without an operator end, even if it's a nullsub. ---------------------------------------------------------------------- Leledumbo · 2015-07-27 15:59 I kinda like this extension. I've seen an implementation in Active Oberon (it's chapter 3 in the language report), which looks neat and VERY easy to switch a compound statement from synchronous to asynchronous and back. ---------------------------------------------------------------------- Basile B. · 2015-07-27 17:49 I already see a grammatical conflict: Code: [Select]program Project1; uses classes; begin(TMemoryStream.Create)        .size := 8; end. So parenthesis would not be the best candidates for a begin expression because some valid code could already be typed in a parens pair following the begin. ---------------------------------------------------------------------- Leledumbo · 2015-07-28 07:14 Quote from: BBasile on July 27, 2015, 07:49:00 pmI already see a grammatical conflict: Though easily solved (well, our parser is hand written), I agree with you. I prefer something distinct, probably using directive syntax, so there would be another directive scope: block, other than existing global and local scope. ---------------------------------------------------------------------- nomorelogic · 2015-07-28 10:28 i think this is very interesting http://www.elementscompiler.com/elements/oxygene/language.aspx#paralle lism under "Asynchronous Statements" section you can find this: Code: [Select]begin   …   async begin     DoThis();     DoThat();   end;   DoSomethignElse();   … and... QuoteHere, the entire "begin"/"end" block with the calls to DoThis and DoThat will be executed asynchronously, while the current thread continues on to call DoSomethingElse. ---------------------------------------------------------------------- taazz · 2015-07-29 16:40 Quote from: nomorelogic on July 28, 2015, 12:28:28 pmi think this is very interesting http://www.elementscompiler.com/elements/oxygene/language.aspx#paralle lism under "Asynchronous Statements" section you can find this: Code: [Select]begin   …   async begin     DoThis();     DoThat();   end;   DoSomethignElse();   … and... QuoteHere, the entire "begin"/"end" block with the calls to DoThis and DoThat will be executed asynchronously, while the current thread continues on to call DoSomethingElse. hell no. That is more confusing because being inlined gives the false information that DoSomethingElse will be called after the to dothis dothat is over and farther more why would I need to inline async calls? what do I get out of it? for example I can see the benefit of an OTL library which hides all that thread and synchronization primitives helping you focused on your problem but I don't see anything useful on this. ---------------------------------------------------------------------- nomorelogic · 2015-07-29 18:11 Quote from: taazz on July 29, 2015, 06:40:39 pmhell no. That is more confusing because being inlined gives the false information that DoSomethingElse will be called after the to dothis dothat is over and farther more why would I need to inline async calls? what do I get out of it? for example I can see the benefit of an OTL library which hides all that thread and synchronization primitives helping you focused on your problem but I don't see anything useful on this. I think keyword "async" serves precisely to eliminate this kind of confusion. Basically there is not much difference between this asynchronous inline code and: (1) create a thread, (2) run it and (3) do something else in the meantime (no wait for). I refer, as an example, to a remote soap authentication or run a heavy query against a database (which takes time)  that keeps main thread locked. To remedy I can create a thread that authenticates or write something like: Code: [Select]TForm1.Create (Sender: TObject); begin     async Datamodule1.OpenAllNeededTables;     async Soap1.Authenticate;     Left:=100;     Caption:='Hello World!'; end; or Code: [Select]TForm1.Create (Sender: TObject); begin     async begin       Datamodule1.OpenAllNeededTables;       Soap1.Authenticate;    end;     ... end; ---------------------------------------------------------------------- bylaardt · 2015-07-29 19:59 i agree with taazz: confusing. what async do? insert something like a application.processmessages after each instruction at compiler time? ---------------------------------------------------------------------- nomorelogic · 2015-07-30 13:31 Quote from: bylaardt on July 29, 2015, 09:59:47 pmi agree with taazz: confusing. what async do? insert something like a application.processmessages after each instruction at compiler time? async should execute, asynchronously, a single statement or a begin/end block (in a separate thread) ProcessMessages forces main thread messages to be executed honestly I think that confusion there is and can be dangerous, in relation to code as: Code: [Select]for a:=1 to 100000 do   async DoSomething(); this code is expected to create 100000 threads: here the damage is greater than the benefit where the rigth code to write is: Code: [Select]async for a:=1 to 100000 do   DoSomething(); but I think it's still an interesting, easy and powerful approach ---------------------------------------------------------------------- bylaardt · 2015-07-30 14:26 ok, but in theory i can't see relevant differences between your async and Code: [Select]procedure ICanWait; begin   for a:=1 to 100000 do     DoSomething; end; procedure DoSomething begin   while {condition} do     begin       MoreSomethings;       application.processmessages;     end end; Maybe Async compile uncontrolled processmessages in every places... PS: IMHO, in C (and others like C languages) and VB async have no relevance without await. The syntax is not confuse... Confuse is the moment when others OS Messages must be called to not freeze your application. ignoring syntax, what async realy do? if the answer is "prevet freeze" then is confusing because application.processmessages do it too and you have the control when the interrupt must occurs. if the answer is "paralel processing" then is more confusing because async runs over the same application thread! ---------------------------------------------------------------------- Basile B. · 2015-07-31 13:18 Quote from: Cooler on July 27, 2015, 01:34:25 pmQuote from: BBasile on July 27, 2015, 01:52:32 amTo make it more versatile (not only dedicated to TCriticalSection, which the compiler doesn't know BTW, it's a library type... ), I'd see more this as an operator that could be overloaded for a particular type, for example: Without try/finally it's pretty useless (unless end operator is always executed). Also such generic approach doesn't allow optimization: compiler will always generate code even if it's redundant. Not true, such a construct is a template. It means that it got to be expanded to build the AST. From far before the optimization phase. ---------------------------------------------------------------------- JorgeAldo · 2015-07-31 18:16 we need aspect oriented programming extensions.