<commit_before>// ffmpeg_player.cpp
// part of MusicPlayer, https://github.com/albertz/music-player
// Copyright (c) 2012, <NAME>, www.az2000.de
// All rights reserved.
// This code is under the 2-clause BSD license, see License.txt in the root directory of this project.

#include "ffmpeg.h"

#include <stdio.h>
#include <string.h>
#include <boost/bind.hpp>


#define SAMPLERATE 44100
#define NUMCHANNELS 2


bool PlayerObject::getNextSong(bool skipped) {
	PlayerObject* player = this;
	
	// We must hold the player lock here.
	bool ret = false;
	bool errorOnOpening = false;
	
	PyObject* oldSong = player->curSong;
	player->curSong = NULL;

	{
		PyScopedGIL gstate;
		
		if(player->queue == NULL) {
			PyErr_SetString(PyExc_RuntimeError, "player queue is not set");
			goto final;
		}
		
		// Note: No PyIter_Check because it adds CPython 2.7 ABI dependency when
		// using CPython 2.7 headers. Anyway, just calling PyIter_Next directly
		// is also ok since it will do the check itself.
		
		player->curSong = PyIter_Next(player->queue);
		
		// pass through any Python errors
		if(!player->curSong || PyErr_Occurred())
			goto final;
	}

	{
		if(tryOvertakePeekInStream()) {
			// nothing needs to be done anymore!
			ret = true;
		}
		else if(!player->openInStream()) {
			// This is not fatal, so don't make a Python exception.
			// When we are in playing state, we will just skip to the next song.
			// This can happen if we don't support the format or whatever.
			printf("cannot open input stream\n");
			errorOnOpening = true;
		}
		else if(!player->inStream) {
			// This is strange, player_openInputStream should have returned !=0.
			printf("strange error on open input stream\n");
			errorOnOpening = true;
		}
		else
			// everything fine!
			ret = true;
	}
	
	// make callback onSongChange
	if(player->dict) {
		PyScopedGIL gstate;

		PyObject* onSongChange = PyDict_GetItemString(player->dict, "onSongChange");
		if(onSongChange && onSongChange != Py_None) {
			PyObject* kwargs = PyDict_New();
			assert(kwargs);
			if(oldSong)
				PyDict_SetItemString(kwargs, "oldSong", oldSong);
			else
				PyDict_SetItemString(kwargs, "oldSong", Py_None);
			PyDict_SetItemString(kwargs, "newSong", player->curSong);
			PyDict_SetItemString_retain(kwargs, "skipped", PyBool_FromLong(skipped));
			PyDict_SetItemString_retain(kwargs, "errorOnOpening", PyBool_FromLong(errorOnOpening));
			
			PyObject* retObj = PyEval_CallObjectWithKeywords(onSongChange, NULL, kwargs);
			Py_XDECREF(retObj);
			
			// errors are not fatal from the callback, so handle it now and go on
			if(PyErr_Occurred()) {
				PyErr_Print(); // prints traceback to stderr, resets error indicator. also handles sys.excepthook if it is set (see pythonrun.c, it's not said explicitely in the docs)
			}
			
			Py_DECREF(kwargs);
		}
	}
	
	openPeekInStreams();
	
final:
	{
		PyScopedGIL gstate;
		Py_XDECREF(oldSong);
	}
	if(!ret) {
		PyScopedUnlock unlock(this->lock);
		player->inStream.reset();
	}<commit_msg>fix possible deadlock<commit_after>// ffmpeg_player.cpp
// part of MusicPlayer, https://github.com/albertz/music-player
// Copyright (c) 2012, <NAME>, www.az2000.de
// All rights reserved.
// This code is under the 2-clause BSD license, see License.txt in the root directory of this project.

#include "ffmpeg.h"

#include <stdio.h>
#include <string.h>
#include <boost/bind.hpp>


#define SAMPLERATE 44100
#define NUMCHANNELS 2


bool PlayerObject::getNextSong(bool skipped) {
	PlayerObject* player = this;
	
	// We must hold the player lock here.
	bool ret = false;
	bool errorOnOpening = false;
	
	PyObject* oldSong = player->curSong;
	player->curSong = NULL;

	{
		PyScopedGIL gstate;
		
		if(player->queue == NULL) {
			PyErr_SetString(PyExc_RuntimeError, "player queue is not set");
			goto final;
		}
		
		// Note: No PyIter_Check because it adds CPython 2.7 ABI dependency when
		// using CPython 2.7 headers. Anyway, just calling PyIter_Next directly
		// is also ok since it will do the check itself.
		
		player->curSong = PyIter_Next(player->queue);
		
		// pass through any Python errors
		if(!player->curSong || PyErr_Occurred())
			goto final;
	}

	{
		if(tryOvertakePeekInStream()) {
			// nothing needs to be done anymore!
			ret = true;
		}
		else if(!player->openInStream()) {
			// This is not fatal, so don't make a Python exception.
			// When we are in playing state, we will just skip to the next song.
			// This can happen if we don't support the format or whatever.
			printf("cannot open input stream\n");
			errorOnOpening = true;
		}
		else if(!player->inStream) {
			// This is strange, player_openInputStream should have returned !=0.
			printf("strange error on open input stream\n");
			errorOnOpening = true;
		}
		else
			// everything fine!
			ret = true;
	}
	
	// make callback onSongChange
	if(player->dict) {
		PyScopedGIL gstate;

		PyObject* onSongChange = PyDict_GetItemString(player->dict, "onSongChange");
		if(onSongChange && onSongChange != Py_None) {
			PyObject* kwargs = PyDict_New();
			assert(kwargs);
			if(oldSong)
				PyDict_SetItemString(kwargs, "oldSong", oldSong);
			else
				PyDict_SetItemString(kwargs, "oldSong", Py_None);
			PyDict_SetItemString(kwargs, "newSong", player->curSong);
			PyDict_SetItemString_retain(kwargs, "skipped", PyBool_FromLong(skipped));
			PyDict_SetItemString_retain(kwargs, "errorOnOpening", PyBool_FromLong(errorOnOpening));
			
			PyObject* retObj = PyEval_CallObjectWithKeywords(onSongChange, NULL, kwargs);
			Py_XDECREF(retObj);
			
			// errors are not fatal from the callback, so handle it now and go on
			if(PyErr_Occurred()) {
				PyErr_Print(); // prints traceback to stderr, resets error indicator. also handles sys.excepthook if it is set (see pythonrun.c, it's not said explicitely in the docs)
			}
			
			Py_DECREF(kwargs);
		}
	}
	
	openPeekInStreams();
	
final:
	{
		PyScopedGIL gstate;
		Py_XDECREF(oldSong);
	}
	if(!ret) {
		PyScopedGIUnlock gunlock;
		PyScopedUnlock unlock(this->lock);
		player->inStream.reset();
	}
{'fraction_non_alphanumeric': 0.11190555370801464, 'fraction_numerical': 0.006651147322913203, 'mean_word_length': 3.7324940991345397, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 37, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5170534', 'n_tokens_mistral': 2315, 'n_tokens_neox': 1988, 'n_words': 576}