Home
Tutorials
Code Snippets
Code Samples
Downloads
Links

The Blog
Our Projects
About
Contact

::Add RageStorm to Favorites!::

The Blog | Our Projects | Guest Book | About | Contact

 
Tutorial - Embedded Python (Part 2)
Author:Arkon
Category:Miscellaneous
Uploaded at:10-Dec-03
Views:18247

Intercepting CTRL+C:

I thought that intercepting the CTRL+C would be a piece of cake, well it is, but after a bit of research...
I am going here to speak of how to use it only in Windows, have no idea how it should work in other platforms.

First thing to do is to set up a console window, this is where the user will enter his input.

AllocConsole();

Now after that you initialize the interpreter, make sure you call this AFTER you can AllocConsole (so it seems - the interpreter will know of it. I am not sure why. Anyways, I was looking at the source code of Python and tried to see what's going on, without luck.)

Py_Initialize()

That was it for start. Now in the main loop of the shell we would use ReadFile to get user's input.

while {
 ret = ReadFile(GetStdHandle(STD_INPUT_HANDLE), &UserInput, 1024, &BytesRead, NULL);
 if (!ret) break;
 if (BytesRead == 0) { // Nothing was entered.
  if (GetLastError() == ERROR_OPERATION_ABORTED) {
   PyEval_RestoreThread(tstate); // Acquire the GIL before we call a Python API.
   if (PyErr_CheckSignals() != 0) { // Any problem, was CTRL+C pressed?
    PyErr_Print(); // Yeah, so print the exception's output.
    tstate = PyEval_SaveThread(); // Release the GIL.
    continue; // Back to read a new line...
   }
   tstate = PyEval_SaveThread();
  } else break; // Outta of here if it were an end of line marker (simply CTRL+Z).
 }
}

Internal Python Threading Support:

In general, there is a GIL(Global Interpreter Lock) which is responssible for what you (or the interpreter) can do when. Technically, it's mechanism to protect the global pointer to the current running thread state. SaveThread will store the current(main thread in our case) thread state, and release the GIL, which causes to execution of OTHER threads. Laters, when you want to run a code (you have to acquire the GIL) you have to restore it to the last state of the main thread.

Here is a few instructions of how to set it up and use it with your own code. Upon initialization just after calling Py_Initialize() you would do:

PyThreadState *tstate = PyEval_SaveThread();

Everytime you run a piece of embedded Python code(from C/C++ or whatever) or user-input code you would do:

PyEval_RestoreThread(tstate);
PyRun_SimpleStringFlags(UserInput, &cf);
tstate = PyEval_SaveThread();

And when you shutting down, you better call

PyEval_RestoreThread(tstate);

and follows this you can call all de-init functions which require an access to Python objects and last Py_Finalize().

Let's give it a shoot:

Py_Initialize()
InitModule1();
InitModule2();
tstate = PyEval_SaveThread();

while {
 ReadFile(GetStdHandle(STD_INPUT_HANDLE), &UserInput, 1024, &BytesRead, NULL);
 PyEval_RestoreThread(tstate);
 PyObject* pyRet = PyRun_String(UserInput, Py_single_input, globals, globals);
 if (PyErr_Occurred()) PyErr_Print();
 Py_XDECREF(pyRet);
 tstate = PyEval_SaveThread();
}

PyEval_RestoreThread(tstate);
KillModule2();
KillModule1();
Py_Finalize();

Note: C threads cannot just run Python C/API code whenever they want to, because they don't have the GIL and a thread state in first place, every C thread have to exist in the interpreter by creating a thread state for itself, acquiring the GIL and then just using it like the above example, not forgetting to release it and free objects when done.

At last if you would run the following code:

import threading
def x(): print "xxx"
threading.Timer(2, x).start()

It would really print "xxx" after 2 seconds. What's so special of it? To be honest nothing, but you can see that if you don't support threading, executing this code without doing anything else will never run this code. So what you can do is "for i in xrange(1000): print i" and suddenly after the loop started it would print the "xxx"...


Blocking IO:

There are two macros which will do the work for you, everytime you are doing a blocking IO you can save the thread state and when you're done you acquire it, so when you don't need Python-execution-time you just leave it to other threads.

For example:

PyObject* BlockingFunction(PyObject* pSelf, PyObject* pArgs)
{
 Py_BEGIN_ALLOW_THREADS;
 accept(socket, ...);
 Py_END_ALLOW_THREADS;
 Py_INCREF(Py_None);
 return Py_None;
}

Your Very Own Types

These are a few valuable pages I found, all credit reserved to: Michael P. Reilly.

NOTE:
Comments that will hurt anyone in any way will be deleted.
Don't ask for features, advertise or curse.
If you want to leave a message to the author use the contacts,
if you have any question in relation to your comments please use the forum.
Comments which violate any of these requests will be deleted without further
notice. Use the comment system decently.

Post your comment:
Name:
email:
Comment:
::Top 5 Tutorials::
Embedded Python[116190]
2D Rotated Rectangles Collision Detection[88422]
Keyboard Hook[76639]
UDP[65618]
HTTP Proxy[41009]

::Top 5 Samples::
2D ColDet Rotated Rectangles[11521]
PS2 Mouse Driver[6918]
Wave Format Player[5754]
Reading FAT12[5581]
CodeGuru[5319]


All rights reserved to RageStorm © 2009