Jump to content

[Delphi] Bindings: Prototypen


Recommended Posts

Ich werfe mal in die Arena einen Entwurf (Delphi7 Prof., seit 2002) zumindest der kompletten Haupt-Klasse IPConnection, ohne die nix geht und die Komponenten BrickStepper(unvollständig) und BrickletIO4 (vollständig).

 

Basierend auf der C#-Implementation, wurde versucht die Struktur weitgehends ähnlich umzusetzen. (Ausnahme z.B. die Delegates in C#). Hinzugefügt habe ich einige Basis-Klassen bzw. Typen oder Records, wo es sich u.U. anbietet den Code zu vereinfachen, um z.B. Redundanzen zu vermeiden.

 

Ob das implem. threadsichere FIFO-Pattern (BlockingQueue) so zuverlässig ist wie in C# oder Java sollte als erstes begutachtet werden.

 

Die Units lassen sich prima zu einer x32-Exe compil. und zumindest auf einem i5,Win7x64 ausführen.

Ob es mit älteren bzw. jüngeren Delphi-Versionen als D7 compilierbar ist, muss geprüft werden.

 

Es wird kein Anspruch auf Vollständigkeit erhoben noch die lehrbuchmässige Umsetzung von Delphi-Code.

Das alles hat experimentellen Charakter und sollte erstmal nur von erfahrenen Delphi-Entw. in einem Code-Review gesichtet und getestet werden. Verwendung des Source-Codes also auf eigenes Risiko !

entwurf.zip

Link to comment
Share on other sites

Ich befürchte TryDequeue ist noch nicht korrekt gelocked (habe aber auch Probleme Delphi zu lesen ^^):

        while (Count = 0) do begin
          state := MsgWaitForMultipleObjects(1,job,false,timeout,QS_ALLINPUT);
          if (closing) or (timeout < Timeout_Infinite) or (state=WAIT_TIMEOUT) then begin
           result := false;
           exit;
          end;
          timeout := tick-GetTickCount;
        end;

 

Ich verstehe nicht ganz wie, aber wenn ich es richtig sehe ist das der Code der wartet bis mindestens ein Eintrag in der Queue steckt (das ist jetzt eine Annahme von mir).

 

Daraufhin kommt dieser Code:

writeLock.Acquire;
        try
          p:=queue.Pop;
          item := p^;
          dispose(p);
        finally
          writeLock.Release;
        end;

 

Das Problem ist, dass zwischen dem Aquire und dem Verlassen der while-schleife darüber kein locking besteht. Es ist also möglich, dass mehrere Threads die while-schleife verlassen, dann bekommt der erste Thread das writeLock (Thread 2 hängt jetzt am aquire), holt sich das einzige Item in der Queue und gibt das lock wieder ab. Jetzt kann der zweite Thread weiterlaufen, beim Pop knallt es jetzt aber.

 

Der C#-Code funktioniert deswegen, weil die gesamte Schleife in einem locked-Bereich liegt, aber das Monitor.Wait es zulässt, dass man sein Lock mittendrin wieder abgibt und auch wiederbekommen kann.

 

Du müsstest also sicherstellen, dass man vor dem Prüfen des count das lock aquired, und es wieder abgibt und wartet falls der count == 0. Falls der count > 0, dann sollte man das lock behalten, poppen gehen und danach das lock abgeben.

 

Viele Grüße

Jan

Link to comment
Share on other sites

Anfangs hatte ich die CriticalSection (WriteLock) zu Beginn und Ende von TryDequeue eingetragen, aber das führte dazu, dass der HauptThread, der nur dort reinläuft, das Enqueue vom Recv-Thread aber solange blockiert, bis die 2.5 sec abgelaufen sind. Das führte dazu, adss nie ein Device geadded wurde.

 

Deshalb der Lock erst später wenn von der Queue gepoppt wird. Innerhalb der Zeitschleife wird aber auf das geblockte Count geprüft und zur Laufzeit wird dort die Count = 1 aber festgestellt.

 

Das lock(writeLock) in C# entspricht m.W. den Acquire und Release in Delphi

writeLock.Acquire;
try
...
finally
  writeLock.Release;
end;

 

Das Monitoring wird in Delphi 7 noch nicht unterstützt, mir ist allerdings noch ist ganz klar wie man das durch D7 Bordmittel alternativ lösen könnte. Aber ich schaue mir mal Deinen Ansatz heute abend genauer an. Danke fürs Code Review.

 

Hmmh, ich sehe gerade das die Funktion Count in den C#Bindings überhaupt nicht benutzt wird, es wird immerzu das Property queue.count abgefragt, hat das ev. mit der o.g. Situation zutun ?

Link to comment
Share on other sites

Hmmh, ich sehe gerade das die Funktion Count in den C#Bindings überhaupt nicht benutzt wird, es wird immerzu das Property queue.count abgefragt, hat das ev. mit der o.g. Situation zutun ?

Die Funktion count macht ja nicht mehr als innerhalb einer critical-section den queue.count abzufragen. Da queue.count sonst auch nur innerhalb der locks verwendet wird ist das äquivalent.

 

Das lock(writeLock) in C# entspricht m.W. den Acquire und Release in Delphi

writeLock.Acquire;
try
...
finally
  writeLock.Release;
end;

Tatsächlich sieht der vom C#-Compiler generierte Code ziemlich genau so aus (abgesehen von anderen Locking-Klassen ;))

 

Anfangs hatte ich die CriticalSection (WriteLock) zu Beginn und Ende von TryDequeue eingetragen, aber das führte dazu, dass der HauptThread, der nur dort reinläuft, das Enqueue vom Recv-Thread aber solange blockiert, bis die 2.5 sec abgelaufen sind.

Das klingt danach, dass du während des wartens das lock nicht abgegeben hast. Das wäre aber nötig. Ich versuche mal in Pseudo-code darzustellen wie es grob aussehen könnte (nicht schön aber selten):

while(true) {
  lock.aquire();
  if(count > 0) {
    break;
  }

  lock.release();
  WaitSomeTime();
}

pop();
lock.release();

 

In diesem Beispiel habe ich jetzt sowas wie closing und exception-handling mal ausgeblendet. Das ist jetzt nur exemplarisch fürs locking, wenn du sonst keine besseren Bordmittel von Delphi bekommst.

Link to comment
Share on other sites

Dann würde das etwa so aussehen:

begin
    result := false;
    writeLock.Acquire;
    job:=CreateEvent(nil,false,false,nil);
    try
      try
        tick:=GetTickCount + DWord(timeout);
        while (true) do begin

          writeLock.Acquire;
          if (Count > 0) then
            break;
          state := MsgWaitForMultipleObjects(1,job,false,timeout,QS_ALLINPUT);
          writeLock.Release;

          if (closing) or (timeout < Timeout_Infinite) or (state=WAIT_TIMEOUT) then begin
           result := false;
           exit;
          end;
          timeout := tick-GetTickCount;
        end;

        p:=queue.Pop;
        item := p^;
        dispose(p);

        result := true;
      except on E: Exception do
        raise Exception.Create('TBlockingQueue.TryDequeue:' + e.Message);
      end
    finally
      CloseHandle(job);
      writeLock.Release;
    end;
end;

Was ist aber mit dem WriteLock zu Proc-Begin bzw. Ende ?

Link to comment
Share on other sites

Zu der Schleife:

Findet das Warten nicht in der Funktion MsgWaitForMultipleObjects statt? Weil du das lock erst danach freigibst, das heißt also während du wartest darf es sich niemand anders nehmen. Ich würde jetzt vermuten, dass das Release VOR dem MsgWaitForMultipleObjects stehen sollte.

 

Das if(closing) usw sollte wohl lieber zwischen aquire und if(count > 0) stehen, auf jeden fall sollte es nur dort stehen wo du das lock gerade besitzt. Sonst ist es wieder möglich, dass jemand close aufruft, nachdem du auf closing geprüft hast.

Link to comment
Share on other sites

Github wird nicht von TF betrieben sondern ist ne unabhängige Seite die Git-Repositories für andere Leute hostet. Deswegen brauchst du da auch einen eigenen Account.

 

Solltest du bisher nicht mit Git oder anderen Versionsverwaltungssystemen vertraut sein, kannst du dich ja auf http://help.github.com erstmal grundsätzlich damit vertraut machen. Allerdings gibt es definitiv eine gewisse Einstiegshürde, wenn man noch nie vorher Versionsverwaltung betrieben hat.

Link to comment
Share on other sites

SVN und Jira Erfahrungen.

 

Gab es oder gibt es hier im TF-Portal jemals eine kurze Einweisung wie wir Github im Hinblick auf Mitarbeit beim Source-Code für TF-Produkte zu benutzen haben ?

 

Sicher kann ich mir die Online-Hilfe reinziehen, aber das klärt nur die techn. Benutzung von Github, aber wie soll z.B. die Source-Code Formatierung aussehen, welche Ansprüche hat man zwecks Organisation und Handhabung der Sourcen für die TF-Produkte etc...

 

Und wenns dann mal mehr Aufklärung gegeben hat, welchen der Account-Arten von Github habe ich zu wählen um für die TF-Gemeinde "produktiv" zu sein ?

Reicht der kostenlose OpenSource Acc ? Oder sind damit Einschränkungen im TF-Repos zu rechnen ?

 

Wäre es gar möglich zwecks guten Kundenservice die TF-Accounts gleich auch autom. bei Github anzulegen ? Sozusagen Github transparent im TF-Portal ist.

 

Link to comment
Share on other sites

Letzteres ist technisch nciht möglich und ich vermute sogar gegen die Bedingungen von Github (automatisches Anlegen von Accounts). Zumal ja nur ein Bruchteil der Forenuser auf Github was beisteuern wird und dann noch viele schon vorher einen Account hatten (wie ich).

 

Code-formatierungen wird es von TF wohl zu nicht-unterstützten Sprachen kaum geben. Ansonsten reicht aber ein kostenloser Account völlig aus.

 

Grundprinzip bei allen Github-Projekten sieht so aus:

Du gehst auf das öffentliche Repo, dann forkst du es und führst alle Ändeurngen an deiner eigenen Kopie durch. Wenn was dabei ist was du für teilenswert hälst, dann kannst du ne "Pull Request" stellen, das ist quasi die Bitte an TF deine Änderungen zu übernehmen. Dann kann TF sich das anschauen, villt noch mit dir Rücksprache halten und am Ende übernehmen.

 

Das war Github in Kurzform. Falls es jetzt noch Organisatorisch was gibt kann TF das ja nachtragen, aber meiner Erfahrung nach ist es recht unkompliziert ^^

Link to comment
Share on other sites

  • 4 weeks later...

Wird hier noch weiter gearbeitet?

 

Edit01

Auf den Ersten Blick sieht der Code Recht gut aus. Schön aufgeräumt. Müsste mit wenige Änderungen auch unter Lazarus/FPC laufen. z.b. die unit windows muss mit Compiler-Deriktiven umlammert werden.

 

Vielleicht sollten wir versuchen, gemeinsam den Code voran zu treiben?

 

Edit02: "ScktComp, Sockets" werden nicht gefunden. unter Lazarus 1.1 Ubuntu 12.4 Beta 2

 

Edit03: Ich finde kein Ersatz oder unit für "CreateEvent". Vielleicht war ich etwas zu voreilig.

Link to comment
Share on other sites

Ich kann es nun Komplieren. Bei einigen Stellen musste ich die Parameter Ändern. Du hattest Globale Variablen genommen, als Lokalen Variablen für Funktionen. Außerdem musste ich bei allen Event's Zuweisungen ein @ davor machen. Und die Unit Windows konnte ich teilweise durch LCLType ersetzten. wegen "Short".

 

Nun habe ich da Probleme, womit ich gerechnet habe: Es gibt noch keine Socktet Komponenten unter Lazarus. Jedenfalls habe ich noch keine gefunden bis jetzt.

 

edit01: fpSock gibt es....

 

Edit02: Ich habe auch nur das "Starter-Kit"... Daher werde ich wohl eigene Units für meine Bricks erstellen z.b. für das LCD-Brick.

Link to comment
Share on other sites

Klasse pluto und danke für dein Engagement.

 

Welche globale Variablen waren das ?

Wie war es denn mit der BlockingQueue ? Kannst Du die compilieren ?

 

Eig. arbeite ich an den Bindings nicht mehr weiter, ich möchte photron nicht dazwischen funken, m.W. ist er an den Delphi-Binding augenblicklich dran.

 

Könntest Du Deine Veränderungen hier mal reinstellen, ich würde den Code mal unter D7 ausprobieren.

Gibt es unter FPC eine VCL, d.h. gibt es visuelle Komponenten ?

Link to comment
Share on other sites

Klasse pluto und danke für dein Engagement.

Ich war leider etwas voreilig. Ich habe mir gedacht: ich warte einfach ab, bis es eine Delphi Anbindung gibt. Solange nutzte ich Lazarus und PHP zum "Testen". z.b. kann ich ein PHP Script erstellen, welches den LCD Steuern kann. Das wird dann von Lazarus aufgerufen.

Aber ich werde gerne auf Fragen Antworten. Von Zeit, zu Zeit werde ich versuchen mich weiter mit TCP/IP unter Lazarus zu befassen. Leider habe ich kein Einstig in das Thema gefunden.

 

Ich wollte erst mal eine Verbindung zum BrickD herstellen und einfach nur "lauschen", was da ankommt und wie die "unterhaltung" zwischen BrickV und BrickD aussieht.

 

Welche globale Variablen waren das ?

Es waren verschiedene. Meistens beim constructor. Ich weiß nicht wie Delphi das Handhabt.

 

Wie war es denn mit der BlockingQueue ? Kannst Du die compilieren ?

Leider nicht vollständig: ich musste TBlockingQueue.TryDequeue ausklammern.

CreateEvent habe ich leider nicht.

 

Eig. arbeite ich an den Bindings nicht mehr weiter, ich möchte photron nicht dazwischen funken, m.W. ist er an den Delphi-Binding augenblicklich dran.

Ach so, dass wusste ich nicht. Aber, es kann ja nicht schaden,wenn er den Genarator gleich für FPC auslegt. Die Probleme sind "Minimal" und kann man gut "Auslagern", denke ich.

 

Könntest Du Deine Veränderungen hier mal reinstellen, ich würde den Code mal unter D7 ausprobieren.

Findest du im Anhang.

 

Gibt es unter FPC eine VCL, d.h. gibt es visuelle Komponenten ?

Das auch. Aber ich nutzte Lazarus. Lazarus bietet die LCL an, die Kompatibel ist zu VCL. Kompatibel heißt in diesen Sinn: Theoretisch. In der Regel klappt es. Im FPC gibt es wohl auch einige Packete, wo mit du auf GTK2 und QT und die Windows API zugreifen kannst, aber über Lazarus ist es deutlich einfacher.

 

Ich finde dieses Baukasten System einfach Toll. Gut "Mängel" gibt es immer, aber nur kleine... aber das ist ein anders Thema. Auch das es schon für viele Programmiersprachen schon Anbindungen gibt ist Klasse. Da ist immer eine runter die man kann. Bei mir ist es PHP. Jedenfalls mehr als Java.

 

Edit1: Den Anhang vergesen. Unter Delphi7 wirst du ihn so nicht Kompilieren können. Du hast keine LCLType und LCLInft und der gleichen. Die müssten in Compilier-Schalter

entwurf_pluto.zip

Link to comment
Share on other sites

Ich wollte erst mal eine Verbindung zum BrickD herstellen und einfach nur "lauschen", was da ankommt und wie die "unterhaltung" zwischen BrickV und BrickD aussieht.

 

Das ist nicht möglich, so funktionieren Sockets nicht. Du kannst dir aber mit Programmen wie Wireshark angucken was auf der Verbindung passiert!

Link to comment
Share on other sites

Das ist nicht möglich, so funktionieren Sockets nicht. Du kannst dir aber mit Programmen wie Wireshark angucken was auf der Verbindung passiert!

Danke für die Antwort. Ich dachte mir schon, dass es so nicht geht. Ich habe es mal mit telnet versucht, die Verbindung "abzuhören". Damit konnte ich nur das sehen, was BrickV mit BrickD ausgetauscht hat, und auch nur einmal beim Verbinden.

 

Aber wie macht das denn "Wireshark"?

Link to comment
Share on other sites

Das meine ich nicht. Linux unterstützt das ja. Die Frage geht eher: Wie geht das?

Wie kann ich auf die Sockets zugreifen mit FPC/Lazarus?

 

Mir ist klar, ich muss ein Port Öffnen und dann Lauschen. Ich lese immer wieder das man Ports wie Dateien öffnen kann. Habe ich aber noch nie geschafft.

Link to comment
Share on other sites

Sag mal willste uns veräppeln  :o

nach 10 sec Suche über Google

http://wiki.freepascal.org/Sockets

 

Ich kann mir auch nicht vorstellen, daß solche elementaren Bausteine wie Sockets in einer Programmiersprache nicht vorhanden sind...Ansonsten einfach mal googlen.

 

Und welchen Constructor in den Delphi-Bindings meinst du, da gibt es zahlreiche...

Mach mal bitte ein Codeausschnitt hier rein, wo du vermutest eine globale Var. wird benutzt.

 

Hab mir Deine Änderungen mal angeschaut: Du meinst ev. TBaseThread, dort ist eine (private) Klassenvariable mit Namen ipconn, ebenso heißt aber auch das Argument im Klassenkonstruktor.

Link to comment
Share on other sites

nach 10 sec Suche über Google

Ja die habe ich auch gefunden, nur gibt es keinen Code mehr dazu.Jedenfalls habe ich keinen gefunden.

 

Ich kann mir auch nicht vorstellen, daß solche elementaren Bausteine wie Sockets in einer Programmiersprache nicht vorhanden sind...Ansonsten einfach mal googlen.

Es gibt bestimmt auch was. Aber das muss ich erst finden. Ich habe ja auch schon das eine oder andere Gefunden, nur gibt es dazu keine Beispiele. Z.B. LNet oder Synapse oder halt ftsocks und sowas.

Es gibt da auch im FPC-Ordner einige Sachen zu, aber ich weiß noch nicht wie man damit umgeht.

Von Zeit zu Zeit werde ich aber mal weiter suchen. Das Problem ist auch:Das meiste ist leider in Englisch und das kann ich kaum.

(Ich weiß ein Widerspruch an sich)

 

Und welchen Constructor in den Delphi-Bindings meinst du, da gibt es zahlreiche...

Überall wo ich ein A an den Parameter vorgeschoben habe.Aber ich kann eine Liste erstellen.

 

Hab mir Deine Änderungen mal angeschaut: Du meinst ev. TBaseThread, dort ist eine (private) Klassenvariable mit Namen ipconn, ebenso heißt aber auch das Argument im Klassenkonstruktor.

Z.B. Damit kommt FPC nicht zurecht, jedenfalls höchsten nur im Delphi-Modus.

 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...